4 ms·
What may not be obvious here is that the Haskell function in each of these situations is the exact same generic function. The flow of data is abstracted via the
by chousuke 9y ago
What may not be obvious here is that the Haskell function in each of these situations is the exact same generic function. The flow of data is abstracted via the Monad interface, so that it becomes possible to write code that works generically regardless of whether parallelism, IO, databases or whatever else is involved. This makes testing such functions quite pleasant.
- tom_mellior 9y agoBut is writing such functions, and all the auxiliaries, pleasant as well? What type would I have to give the "getData" function to work in all these examples? What if I just want to call an existing getData function that returns a Maybe, not some generic monad thing? Would I have to tweak the syntax for all the different monads I want to call it from? Also, what would the print call really look like in actual code? This post talks about composing IO with other monads. I've never seen a monad composition example that wasn't peppered with calls to unintuitively named functions like liftM_. The pseudocode looks nice, but if the pseudocode isn't a faithful representation of actual use, then it's not very useful.
- mabbo 9y agoYou don't necessarily need to implement each function perfectly generically. You maybe could, but that's not the point here. The benefit, as I see it, is that the main method doing all of these things is clear in its purpose regardless of which monad is being used. It's moving code more towards "this is what I want" rather than "here's how to do what I want".
- ves 9y agoWe have a library called `mtl` that addresses both abstracting over specific monads in order to achieve polymorphism and do all the `lift`ing ahead of time. You should find some reading material on it, it’s really quite a nice solution! I’m on mobile, otherwise this would’ve been an answer instead of a comment.
- dllthomas 9y agoWriting abstract functions is usually easier than writing concrete functions, because there are fewer ways things can fit together that look anything like correct. (Abstract in this way, in this kind of context...)
- Munksgaard 9y agoMaybe is already a monad.
- dllthomas 9y agoAs I read it, the question was "I have `... -> Maybe a` and I want to use it in a context where I am chaining together some unspecified monad. Will that be awkward?" And the monad interface doesn't help us with that. But the answer is that you have a Maybe, you can do any Maybe thing with it.
- tom_mellior 9y agoYes. That would allow me to use a getData returning a Maybe in the Maybe monad. But I wouldn't be able to use it in the list monad, or the others mentioned in the article.
- dllthomas 9y agoYou could use it in code that's operating in the list monad, you just wouldn't be able to stitch the computation in using the monad interface. But you could run it and get a Maybe back, and look at the Maybe, and turn it into something you can stitch in. `maybe (fail "got back a Nothing") pure` is probably the most generic way, but it may or may not be what you want.
- marcosdumay 9y agoWell, yes. You will need to convert your type into something else to use in another monad. As an example, it's common to track failure with an `Either Text` monad, but functions that can fail on a single way usually have an `Maybe a` return type. That means you will probably have an `Text -> Maybe a -> Either Text a` function around and write code like this: toEither "Error message" $ functionInMaybe a b
- leshow 9y agoYou could use the getData function just fine from anywhere. It's just that you wouldn't be able to use both the list monad instance and the maybe monad instance at the same time without a library like mtl.
- leshow 9y ago> What if I just want to call an existing getData function that returns a Maybe, not some generic monad thing? Maybe is a Monad. In Haskell, Monad is sort of like an interface (called typeclass) that a bunch of types implement. Maybe has an instance of Monad already, same with lists, and continuations, etc.
- chousuke 9y agoThe most common way you generate such generic functions is by using higher order functions like fmap etc, not by directly coding them using do notation, though it is nice to have when you do need it. In languages that have no access to the monad abstraction you'll still need a way to replace your database access with something that you can use during your testing. A monadic database interface won't save you from having to check that your application doesn't do silly things with the database as a side-effect, but it will make it much easier to test that your actual data processing is correct.
- rattray 9y agoHmm, would you be willing to share with us what the typedefs (and ideally implementations) of these `getData` functions might be? Those unfamiliar with Haskell might have a hard time visualizing.
- pacala 9y agoTherein lies the problem. A given piece of code looks the same for any possible monad, but it only provides useful functionality for a specific monad. The reader of the code is left wondering _for what benefit_ the code was written, and may waste some time instantiating it over the wrong concrete monad[s], until it finally clicks. Secondarily, I wish “monads are cool” tutorials would pay some attention on how the do syntax interacts with the rest of the language, and especially with conditionals. For example, assuming a function that computes a value and also collects some side effects, how does one turn the pure variant into a nomadic variant? This is trivial with a mutable collection, but a laborious exercise in monad world.