func fetchAndSave<M<_>: MonadReader<M, Database> & MonadReader<M, HttpClient>
& MonadTask<M> & MonadError<M, Error>>(userId: Int) -> M<()> {
#monadic {
api: HttpClient <- M.ask()
db: Database <- M.ask()
user <- api.fetch(userId: userId)
db.save(user: user)
}
}
Explanation:
// The above implementation doesn't require separate protocols for requiring
// dependencies like the GAT version. Now we're starting to get into some of the
// benefits of HKTs over GATs.
// The main thing that makes this code pretty verbose is the types. If we wrote
// this code with the level of type inference Hindley-Milner type inference
// offers (often found in pure FP languages) we'd be down to:
func fetchAndSave(userId) {
#monadic {
api: HttpClient <- ask()
db: Database <- ask()
user <- api.fetch(userId: userId)
db.save(user: user)
}
}