4 ms·
I'm not sure it's correct to understand monads as a kind of function coloring mechanism. Two reasons. First, monads aren't functions. Second, the whole functio
by mumblemumble 6y ago
I'm not sure it's correct to understand monads as a kind of function coloring mechanism. Two reasons. First, monads aren't functions.
Second, the whole function coloring thing wasn't a complaint about limits on function composition. If it were, we might have to raise a complaint that arity is a kind of color. It was a complaint that functions of one color can't be called from inside functions of another color. And you don't really have an equivalent problem with using one monad inside the implementation of another monad.
- valenterry 6y agoYou have the same problem with monads. You cannot have a (pure) function, that calls another function which returns an IO monad, without returning an IO monad itself _or_ ignoring the result of the function call (which is exactly the same as not calling it from the beginning). Hence the semantics are the same - just without any compiler feature like async/await.
- mumblemumble 6y agoThat's a special case for IO, isn't it? I guess I've never actually tried this, but I don't think it's generally the case for all monads. IOW, Haskell does have coloring, and its coloring does manifest through the IO monad, but that does not mean that all monads have color. Sort of like how you can't do, "Socrates is a man, Socrates is dead, therefore all men are dead."
- valenterry 6y agoI think I see what you mean. However, it is the other way around: some monads are special cases in the way that they have extra methods that allow you to "escape" their context. Like lists: you can fold over them and get out a different result type. But in general you can't do that with monads, in particular not if you just have "any" monad at hand but you don't know which one it is. And IO is one example, parsers would be another one.
- aranchelk 6y agoI agree with what you’re saying, but I think calling them “special cases” takes away from the elegance of the system. Haskell users frequently say things like “the IO monad” or “the list monad”, but probably the more accurate way would be to say the “the monad instance of IO”. Monad is a typeclass (analogous to an interface in OOP or traits in Rust). IO is a type. To make some type a member of monad (or any other typeclass), you just need to write the required functions in an instance declaration. You can make a type a member of as many typeclasses as you want. List is a member of Foldable, Monad, etc. Calling that a special isn’t quite right as it is very common for a type to be a member of many typeclasses. You’re entirely correct in saying the Monad typeclass offers no way to extract values. But there actually is an extract function called unsafePerformIO with type “IO a -> a”. The name says it all: use it and you’ll lose all of the safety normally afforded by keeping data derived from effects inside the IO type.
- valenterry 6y agoYou are totally right, I was a bit sloppy with my language there. Thank you for the detailed explanation.
- sullyj3 6y agoIt's true that you can't extract a value obtained from the outside world from the IO type by design, but, I don't think I'd characterize that as a problem. For any piece of code someone thinks they want to write that involves turning an `IO something` to a `something`, I can show how to solve the problem without doing that with a minimum of fuss. It's actually quite difficult for me to remember why someone would want to do that, having been immersed in this paradigm for so long. For example, if you have, say, `getIntFromSomewhere :: IO Int` and you want to apply a pure function of type `Int -> Int` to it. Supppose the pure function adds one. You might initially think that that's all there is to it, you don't want IO involved in what happens next. But of course, the only reason you would ever do some pure computation is to use the result to have some effect on the world, eg printing it to a console. So actually in a sense, you do want to stay in IO. myIOAction :: IO () myIOAction = do myInt <- getIntFromSomewhere print (addOne myInt) As you say, you can't pull the int out of getIntFromSomewhere without operating in an IO action. But why would we want to, since we obviously want to make use of that value to affect the outside world? And of course, that doesn't mean we have any issues getting that int into a pure function `addOne :: Int -> Int` first, before eventually (immediately, in this case) making use of the result to perform some IO.
- dllthomas 6y ago> First, monads aren't functions. Monads aren't functions[0]; in the analogy, monads are colors. > [F]unctions of one color can't be called from inside functions of another color. And you don't really have an equivalent problem with using one monad inside the implementation of another monad. If you have a bunch of `a -> M b` and one `a -> N b`, it's much more straightforward to interleave calls to any group of the former than to incorporate one of the latter. You encounter this when writing a new `a -> M b`, much like you might encounter the corresponding issue when writing a new blue function. I should point out that there is usually good reason for this in the implementation, and often good reason for it in the domain. When there is good reason in the domain, that's great - our type system is surfacing the places we need to make decisions, rather than letting us mash things together in ways that will cause problems. When there is only good reason in the implementation, or no good reason at all, it's unfortunate, but certainly something that can be worked around. Unlike the original setting, Haskell provides the ability to write functions polymorphic in "color" ("polychromatic"?), but for the (unfortunately necessary?) distinction between `a -> b` and `a -> Identity b`, and a lot of colors can be easily removed or transformed. [0]: Some types which are monads are functions but that's not the sense in which we're coloring functions.