Hacker News new | ask | show | jobs
by doyougnu 1029 days ago
Does every usage site have to change? You would alter fibonacci to be:

  fibonacci :: (MonadLogger m, MonadState (Int, Int, Int) m) => m Int
  fibonacci ...
and now of course all callers must support MonadLogger. But instead of using the MonadLogger (or any mtl constraint directly) you should just be constructing an abstraction boundary with a type class synonym:

  class (MonadLogger m, MonadState s m) => MyMonads s m
and now you change fibonacci:

  fibonacci :: MyMonads (Int, Int, Int) m => m Int
  fibonacci ...
And now if you need to add a monad or add Eq or whatever you just have to change your type class synonym rather than every function. Its not a problem with the language its just programing with modularity in mind, even in the type system.
1 comments

I have seen this in the wild. The result often is that every function has a kitchen sink MyMonads constraint of which it only uses a tiny subset. It's death by a thousand cuts. If you make such a class for every monad combination you get insanely large amount of classes. It's simply unworkable. Which is why you get the kitchen sink monad pattern.
If you think it's fine that you can log from all functions in other languages, then what's the problem with adding that constraint to all your Haskell functions to allow this?
The problem is that you should always write your code to be idiomatic in the language. In this case I feel like the Idiomatic Haskell way has serious drawbacks.

For example, It's fine in C to manually allocate/free memory, it's the way you have to write C. It's not fine to do the same thing in Rust. Even though you of course could do that in Rust as well.

It's perfectly idiomatic Haskell to annotate all your functions with an effect you believe they should all have.
The only reason this is idiomatic is because there is no better way. That's the entire point I'm making... Haskell prides itself in writing generic and reusable functions. This then is then thrown out of the window with the kitchen sink monad. Very understandable, because everything else sucks.

That precisely why I think this is a great shortcoming of the language.

It's not a shortcoming of the language; it's a shortcoming of the goal! You can't have both the goal of fine-grained effect tracking and the goal of not having to make fine-grained changes when effects change. They're incompatible goals in any language.

The strength of Haskell is that it allows you to achieve the first goal if you want. Most languages don't (pretty much no other language, actually).

And what's wrong with the kitchen sink monad pattern? I've certainly used exactly that. And I have no problems with it.
Because your code is very much overconstrained at that point. For the same reason you don't add a `Num a` constraint to list `head` function. You have now essentially fused your function to your codebase.
That's not a problem in business logic heavy code. Requirements change and you could use previously unnecessary constraints at any time.