Yeah, its about the type / interface / protocol. But they really are very, very useful. There are just so many problems that can be solved with it.
For example there can be a GraphQL monad. Instead of Promise.all you would use GraphQL.parallel(otherMonads), and the implementation can transform that to a single request to the server instead of multiple ones.
Similarly there can be an ORM monad that avoids the n+1 queries problem. parallel(Users.get(id)).chain(users => ...) will transform the parallel query to a single SELECT ... IN query.
Also, have you tried passing the current user around in server side code, everywhere? Its really tedious to do, and Reader solves that!
A monad transformer is (more or less) a function that takes a monad class and returns a new monad class (yes thats the class object, not an instance). So if for example Future were a monad, and you wanted to add Reader functionality, you would create a new class PromiseWithReader:
var FutureWithReader = ReaderT(Future)
Now you can use this monad to do both getState() and async IO operations within its chained functions.
function getUserPosts() {
return getState()
.chain(state => Posts.getForUserId(state.user.id)) // cheating a bit here
}
in the route handler:
getUserPosts.runWith({user: req.currentUser})
.fork(posts => res.json(posts))
The cool part is you can use getState() at any time to get the state. This will work no matter how deep your functions are: the example above is just with a single function, but that function can call another, which calls another, which calls getState() to get the user. But there is still no need to pass that user through all those calls. And if you use something like a State monad, you'll even be able to modify the contents of the state.
p.s. Future implementation, based on robotlolita's minimal Future class: https://gist.github.com/spion/b433a665ca51d6c38e9f