4 ms·
It's amazing that the concept of "monad" is so simple, and yet the author still got the interface mixed up with one particular implementation (mutable state).
by jmillikin 4y ago
It's amazing that the concept of "monad" is so simple, and yet the author still got the interface mixed up with one particular implementation (mutable state).
Monads are about sequencing. They are an interface to force applying functions in a particular order. This is why Haskell needs them -- in a language with only pure functions, there's no way to encode (for example) the order of 'print' statements directly, it has to be expressed in terms of combining functions.
Consider the interface, expressed in a C-like syntax:
interface Monad {
template<A>
from(value: A) => Self<A>;
template<A, B>
sequence(self: Self<A>, f: fn(A) => Self<B>) => Self<B>;
}
You can see that it forms a sort of one-way valve for functions from one value to another.
Types you could implement that interface for:
* Lists: `from` wraps the value in a size=1 list, and `sequence` becomes "apply the function to each value then concatenate the results".
* Optional: `from` is the case of a value existing, and `sequence` is "if value exists then apply the function, else return None".
* Sets: same as lists.
But it's hard to write a blog post about an interface with only two functions, especially if you've already committed a thousand words to making it seem complicated.
- edflsafoiewq 4y agoMonads are not about sequencing. They don't force applying functions in an order, at least any more than regular function application does. Haskell does not need them, they're just an interface and don't add anything to a language you can't do without them. The order of print statements is encoded the same way the order of elements in a list is, it's part of the definition of the data type. The monad instance says IO (IO a) is "the same as" an IO a, so really any sequencing ability implied in IO (IO a) already pre-exists in IO a.
- nightski 4y agoIt can catch newcomers off guard. People think they write - do a x b x in Haskell and that means the same as a sequential program (a runs before b). But this is not necessarily the case. It does not imply an order. It will only do so if the statements are dependent on each other - do c <- a x b c
- pxeger1 4y agoYou're technically right: a doesn't necessarily run before b, but that's pretty irrelevant. The only case in which it matters which one runs first are if one of the two results in an exception, which is rare especially in idiomatic Haskell code. The effect described by (a x) certainly runs before the effect described by (b x).
- deleted 4y ago[deleted]
- nightski 4y agoDoes it though? If I have the following - do f <- openFile "test.txt" putStrLn "Hello World" contents <- hGetContents handle In this case the file is most likely not opened before Hello World is printed to the console. However it would be opened before contents is read. This is what I mean in that the ordering is not top-down. It is dependency driven. This really tripped me up at first.
- pxeger1 4y agoI am unable to reproduce your claim. My exact code is: import System.IO main = do file <- openFile "hello.txt" ReadMode putStrLn "opened hello.txt" contents <- hGetContents file print contents I ran it with: ghc main.hs strace -fo strace.log ./main The strace log confirms the file is opened before putStrLn writes its output: ... 96748 openat(AT_FDCWD, "hello.txt", O_RDONLY|O_NOCTTY|O_NONBLOCK) = 3 ... 96748 write(1, "opened hello.txt\n", 17) = 17 96748 read(3, "this is a file\n", 8192) = 15 96748 read(3, "", 8192) = 0 96748 close(3) = 0 ... 96748 write(1, "\"this is a file\\n\"\n", 19) = 19 ... Do you have an example that demonstrates IO not happening in the order of the monad? I really don't think what you say can possibly be right, but I'd like to see it if it is.
- nightski 4y agohttps://wiki.haskell.org/Do_notation_considered_harmful https://wiki.haskell.org/Do_notation_considered_harmful This article discusses it further, down around where it says "Newcomers might think that the order of statements determines the order of execution." and then proceeds to give examples further down. I have experienced this in the wild. I was working with a lot of data/csv parsing code along with some heavy computer vision work in haskell (3d reconstruction from point clouds using OpenGL and Repa, and custom algos) about a decade ago. It's been a while but I remember this happening quite well.
- shirogane86x 4y agoYeah I was about to write this. Thinking that monads are only about sequencing works only for IO, effectively. Option and Either are about short circuiting, and there's others that mess in execution state in hilarious ways (like the TARDIS monad and its time travelling shenanigans). I tried "understanding" monads for years but now I'm not really sure if there is anything to understand in particular. It's super simple and super generalized at the same time, it's really hard to explain why it is that way without going deep into scary mathy territory (while also preserving analogies that actually apply)
- afiori 4y agoSequencing here does not mean "building sequences" or "a monad is a sequence", but that the bind/>>= operator build monad values as a sequence of operations. For example with Lists the sequence is not the list itself but the sequence of flatMap[0]/bind/>>= operations necessary to build the desired list starting from `[a]` [0] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
- jmillikin 4y ago> Monads are not about sequencing. They don't force applying functions > in an order, at least any more than regular function application does. Monads are about sequencing. This is easier to visualize if you de-sugar them: f = do x1 <- m 1 x2 <- m 2 return (x1 + x2) is an imperative form of f = m 1 >>= (\x1 -> m 2 >>= (\x2 -> return (x1 + x2))) You can see that there is no way to evaluate `x1` without first evaluating `m 1`, even if the result is a thunk. > Haskell does not need them, they're just an interface and don't > add anything to a language you can't do without them. How would you recommend implementing do-notation without the Monad typeclass? Applicative functors aren't enough. > The monad instance says IO (IO a) is "the same as" an IO a, so really > any sequencing ability implied in IO (IO a) already pre-exists in IO a. I don't see your point. That may be true of IO, but it's not true in general. `Maybe ()` and `Maybe (Maybe ())` are different types.
- simiones 4y ago> You can see that there is no way to evaluate `x1` without first evaluating `m 1`, even if the result is a thunk. Isn't this true of `f(g(x))` as well? You can't evaluate f(g(x)) without evaluating g(x) first, right?
- jmillikin 4y agoYep[0], and if you really wanted to, you could use that syntax to express the monadic interface: putStrLn :: TheWorld -> String -> (TheWorld, ()) main :: TheWorld -> (TheWorld, ()) main world = (part3, ()) where (part1, _) = putStrLn world "Hello" (part2, _) = putStrLn part1 ", " (part3, _) = putStrLn part2 "world!" It would be a frustrating user experience, but it's a valid and equivalent encoding to Haskell's `IO` type. [0] I'm ignoring lazy evaluation for now, but `f` could be implemented such that it returns before `g(x)` is evaluated. This is generally not relevant for purely functional code, but it can affect performance and exception-safety.
- afiori 4y agoLazy evaluation is exactly why Haskell has monads and Ocaml does not. the whole point of haskell was to see what happens if try to build a language with the strong requirement of lazy evaluation. If you remove it you also remove any need* for the IO monad * you can obviously use an IO like interface in a non-lazy language, but it is not necessary
- metanonsense 4y agoYou know that sentence "A monad is a monoid in the category of endofunctors, what’s the problem"? Being a "monoid" is exacly why monads are very much about sequencing.
- deleted 4y ago[deleted]
- jackmott 4y ago