4 ms·
Here's what I don't get with Monads, why I've not felt the need to create a Monad-like system in my (non Haskell) code. The 'world' is carried on the back of o
by sago 11y ago
Here's what I don't get with Monads, why I've not felt the need to create a Monad-like system in my (non Haskell) code.
The 'world' is carried on the back of one parameter, right? So if I want to run a function taking two parameters in a particular 'world', which parameter is the Monad? If both of them are, what if they disagree which world is used?
Perhaps I haven't reached the fabled epiphany of monads, but they seem to be just implicit state attached to a single value, and only a single value. I'm not sure what this implicit stuff is better than a system where you are explicitly passing the 'world' that each function needs.
Why is
roll(dieSides: Random<int>) -> int
Clearer than
roll(random: Random, int dieSides) -> int
I get the passing of a world out of a function with a single value
getNumber() -> IO<int>
or using the extra implicit state to represent other possible values, like
Maybe<int>
but, using it to carry around state? That seems to me to be unnecessarily implicit.
What am I missing?
- prodigal_erik 11y agoIn a language with immutable values and pure functions, roll always returns the same number from the same generator. If you want to call roll twice and get different numbers, the first call needs to return a new generator (with an updated seed) to pass to the second call. Rather than having to write something like do (x, g2) <- roll g1 (y, g3) <- roll g2 (z, g4) <- roll g3 it's convenient to have plumbing that hides all those single-use g values.
- sago 11y agoThanks. So the issue is that Monads save us having multiple returns? That makes sense.
- thinkpad20 11y agoThat's one of the things monads do, but they can do more. A monad is defined by an underlying type (roughly, some kind of "container"), and two functions (putting an object in the container, and creating a new container by applying a function to the contained object). That's all a monad actually is. It simply turns out that given those relatively simply building blocks, you can do all sorts of neato things with them, like encapsulating state, error handling, chaining actions together, etc. An example of a monad which has really nothing necessarily to do with mutable state are Promises in JavaScript. The container, of course, is a Promise, and the two functions I described are `resolve` and `then`.
- sago 11y agoRight, and some of that makes sense to me. It makes sense in some cases (Maybe was the example I gave, a Promised value another) to tie such things directly to a single typed value. But what I was querying was, is this implicitness a good move in general. In particular the issue I was addressing was the encapsulation of an arbitrary state with a single typed value. It's been remarked elsewhere that 'mixing' monads is a problem: I guess that's always been the thing I've felt haven't made them work replicating as a coding pattern. I tend to prefer explicit over implicit state.
- thinkpad20 11y ago> I tend to prefer explicit over implicit state. I guess it depends on your definitions, but monadic state is just as explicit as any other. myMonadicFunction :: String -> State Int String myMonadicFunction string = do currentVal <- get put (currentVal + length string) return (reverse string) It's just as clear as what you'd have in an imperative programming language. I'm convinced the main reason monads aren't common in other languages is that most languages just don't have strong enough type systems to support them explicitly. Implicitly they can be found here or there, such as with promises, or just the fact that the semantics of many imperative languages can be viewed as a monad. But of course, probably the biggest reason is that people who are used to mutable state don't see the need. It's one of those things whose value becomes apparent with use.
- sago 11y agoThanks, I really appreciate your responses. What's to stop you passing a struct or object with the two monadic functions attached, or ignore the function requirement (for the case of state) and pass the struct with the state and result in it? The benefit being that you can package up more than just one value in that (immutable) structure along with the 'specialness'. I don't see why that is less typesafe.
- thinkpad20 11y agoI'm having a tough time trying to understand what you're proposing. First of all, in Haskell there's no real concept of having functions "attached" to objects (in the OO sense). The closest analogue is a type class, which relates a type to a set of functions. For monad, that looks like class Monad m where return :: a -> m a (>>=) :: m a -> (a -> m b) -> m b And it's up to the particular instance to determine how those functions are implemented. For example, instance Monad Maybe where return :: a -> Maybe a return x = Just x (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b maybe >>= f = case maybe of Nothing -> Nothing Just x -> f x The definition of the State type, and its corresponding Monad instance, is a little more involved (I suggest this blog post for an explanation [1]): newtype State s a = State { runState :: s -> (a, s) } instance Monad (State a) where return x = State $ \s -> (x, s) m >>= k = State $ \s -> let (a, s') = runState m s in runState (k a) s' I'm honestly not quite sure what you mean by the rest of it (for example, "ignoring the function requirement", "specialness" etc). But the bottom line is that you can do any typesafe thing you want to, within the rules of how the language works. [1]: http://brandon.si/code/the-state-monad-a-tutorial-for-the-confused/ http://brandon.si/code/the-state-monad-a-tutorial-for-the-co...
- eli_gottlieb 11y agoDon't think of "a monad" as a thing. Think of "monad" as "a way you can threat things": you can treat things monadically by implementing the monad operations for them, such that they obey the monad laws.
- sago 11y agoSo do I. It's a pattern. But you don't create things a particular way for no reason. My point is about being able to see the point. I get the sense that there are people who think Monads are a fundamentally powerful pattern for structuring software. And I don't get that. And I'd like to, because if I'm missing something, it could mean I improve my code architecture. Monad's qualify as a 'that's an interesting way to get around its self-imposed restrictions' moment when learning Haskell (which I've not used with any seriousness) for me. So I don't get the fuss. I'm not saying that I don't see the logic of those restrictions, nor that I don't see why you'd want to voluntarily code with similar restrictions in other languages. More than, if you're going to write something that has to work around those restrictions in another language, I've no idea why you'd necessarily chose Monads. Or, put another way, what use are Monads in non Haskell code?