|
|
|
|
|
by marcosdumay
4269 days ago
|
|
What I'm not liking about this line of reasoning is that in practice Haskell seems to not go far enough[1]. Yes, you have to declare your effects. In practice that means that most of your code returns IO, and isn't constrained anymore. I don't know if this is a library feature, or an essential feature of the language[2], but it would be very interesting for example to put a GUI together by computing events in functions that returned an "Event" monad, widgets in functions that returned a "GUI" monad, database access in functions in a "DB" monad, etc. Instead, all of those operate on IO. [1] A completely subjective assessment.
[2] I've though for a short while on how to code that, but didn't got any idea I liked. |
|
Of course, you can do this as a library. In fact, this is an example use case[1] for Safe Haskell which also prevents people from circumventing your types with unsafePerformIO and friends.
Moreover, some existing libraries already take similar approaches. FRP libraries extract reactive systems (like events but also continuously changing signals) into their own types. A button gives you a stream of type Event () rather than an explicit callback system using the IO type. Check out reactive-banana[2] (my favorite FRP library from the current crop) for a nice example.
Similarly, people use custom monads to ensure things get initialized correctly, which has a similar effect to what you're talking about. The Haskell DevIL bindings for Repa[3] come to mind because they have an IL type which lets you load images and ensures the image loader is initialized correctly exactly once.
Sure, in the end, everything will need to be threaded through IO and main to actually run, but you can—and people do—make your intermediate APIs safer by creating additional distinctions between effects.
[1]: http://www.haskell.org/ghc/docs/7.8.3/html/users_guide/safe-...
[2]: http://hackage.haskell.org/package/reactive-banana
[3]: http://hackage.haskell.org/package/repa-devil