Handling resources

intermediate

Bow Effects also provides functionality to handle resources. Working with resources usually involve acquiring, using, and releasing them regardless of the outcome of their usage.

Consider, for instance, doing a query to a database, regardless of the underlying technology to implement it. It usually involves opening the database, performing the queries that you need to do, and closing the connection.

We can model this with a protocol Database that lets us perform queries that match a predicate and two functions to open and close the database. Note that everything work under Bow Effects IO data type where all these effects are suspended and can be safely composed.

protocol Database {
    func queryUsers(where predicate: (User) -> Bool) -> IO<Error, [User]>
}

func openDatabase() -> IO<Error, Database>
func close(database: Database) -> IO<Error, Void>

We may want to write a function were we query all users that are older than 18 years of age:

func getUsersOver18(from db: Database) -> IO<Error, [User]> {
    return db.queryUsers(where: { user in user.age >= 18 })
}

Now, we can sequence the open-query-close operations using flatMap from Monad:

openDatabase().flatMap { db in
    getUsersOver18(from: db)
        .forEffect(close(database: db))
}

// Or using Monad comprehensions:
let db = IO<Error, Database>.var()
let users = IO<Error, [User]>.var()

binding(
    db    <- openDatabase(),
    users <- getUsersOver18(from: db.get),
          |<-close(database: db.get),
    yield: users.get
)

However, this has a problem. The database needs to be closed regardless of the outcome of the operation getUsersOver18. However, by chaining operations monadically, the close operation will only happen if the previous operations are successful. If there is an error while performing the query, the close(database:) function will not be called.

Bracket

In order to overcome this problem and ensure proper release of the resource, regardless of the outcome of its usage, we can use the bracket method from the Bracket type class. This type class provides this and other methods to work with resources functionally.

We can acquire our data base and invoke bracket, passing two functions: one to release the resource and one to use it:

openDatabase().bracket(release: { db in close(database: db) }) { db in
    getUsersOver18(from: db)
}

Bracket will ensure proper acquisition and release of the resource once we have finished using it.

Resource

The acquire-use-release cycle is so pervasive that there is a data type in Bow Effects that models it and lets us encapsulate the acquisition and release into a single value. This data type is Resource and you can create one by calling its from method, supplying the acquire and release functions:

let dbResource = Resource.from(acquire: openDatabase,
                               release: { db, _ in close(database: db) })

This resource can be used by sending a closure and it will handle opening and closing the database.

let usersOver18: IO<Error, [User]> = dbResource.use { db in
    getUsersOver18(from: db)
}^