Live data from Hacker News

Effectful Haskell: IO, Monads, Functors

slpopejoy.github.io

61–68 of 68 posts

Re: Effectful Haskell: IO, Monads, Functors

#61

Earlier quoted context omitted.

How is the list monad a "computation builder"? Sounds to me like "programmable semicolons" and other nonsense: Monad is a typeclass that means very different things depending on what type you're using. It's the shape that has meaning: this level of abstraction is just going to be hard to grasp correctly no matter what you call it. Likewise with "Mappable": in most other languages, `map` is only an operation on lists…

As a novice programmer used to node.js, C, etc., how the hell is one supposed to get started with Haskell? I've tried multiple tutorials, read many articles like this, and still can't follow along. It makes me wonder who ends up using this language for anything serious in even the slightest time crunch...

Try elm (http://elm-lang.org/) first; it doesn't mention Functor, Applicative, Monoid, Monad anywhere.

You might come back to Haskell later and it won't seem like such a big deal. (I use Haskell for serious work and love that a quick patch doesn't suddenly start breaking things in 10 other places - it's a real time-saver!)

Re: Effectful Haskell: IO, Monads, Functors

#62
post #10
post #7

So is it possible to explain monads with Javascript ? or even LISP ? seems to me monads == haskell so if one doesn't understand haskell one can't understand monads , cause all monad tutorials are written in haskell. So I ask , what is the point of learning what a monad is if i'm writing some Java ?

Yes. Infact, a JavaScript Promise is pretty much the same as the IO monad* - the `then` method is a lot like `bind`. Promise.resolve is a lot like `return` * almost the same to IO, because of recursive thenable assimilation, something that was added to promises but is totally short-sighted and unnecessary; and because promises are eager which means they don't represent the action to be executed, but the value of an a…

I find it interesting that people complain about Haskell's monadic IO, and yet nodejs event-driven IO is arguably the same idea but without the syntactic sugar.

Re: Effectful Haskell: IO, Monads, Functors

#63

Earlier quoted context omitted.

The type isn't the same as its parameter. An IO () is not () just like Maybe () isn't (), and [()] isn't ().

I'm not sure I find that explanation satisfactory. This only implies that there is information not used by the compiler: The only conceivable value that the type IO () can have is still the value IO (). If the compiler knew this, then it could, and would, optimize it away. Is there a formal aspect of parameterized types that prevents this from — formally — happening?

"The only conceivable value that the type IO () can have is still the value IO ()."

There is some magic around the IO type, but it's not needed to answer your question.

If you look in GHC.Types in the package ghc-prim, you can find the definition of IO:

    newtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #))
An IO action is a function from the state of the world, to the state of the world plus a value. So the compiler may know it's giving you (), but it doesn't know what the new state-of-the-world will be.

Re: Effectful Haskell: IO, Monads, Functors

#64

Earlier quoted context omitted.

The type isn't the same as its parameter. An IO () is not () just like Maybe () isn't (), and [()] isn't ().

I'm not sure I find that explanation satisfactory. This only implies that there is information not used by the compiler: The only conceivable value that the type IO () can have is still the value IO (). If the compiler knew this, then it could, and would, optimize it away. Is there a formal aspect of parameterized types that prevents this from — formally — happening?

There's no such thing as "the value IO ()". The type IO () has tons of possible values, which all have different internal representations:

1) The IO action that does nothing.

2) The IO action that prints the string "Hello World" and then returns nothing.

3) The IO action that reads a string from the console, reverses it, prints it back, and then returns nothing.

4) ...

It's a common misconception to think that IO X is a wrapper around X with some "type system magic" to prevent it from being used outside IO. It's nothing like that. For example, IO String is not a wrapper around a String, doesn't contain any String inside, and cannot be converted to String. Meditate on the fact that System.IO.getLine is not a function, but a value of type IO String. Then you will understand why IO () has tons of possible values.

Re: Effectful Haskell: IO, Monads, Functors

#65

Earlier quoted context omitted.

I'm not sure I find that explanation satisfactory. This only implies that there is information not used by the compiler: The only conceivable value that the type IO () can have is still the value IO (). If the compiler knew this, then it could, and would, optimize it away. Is there a formal aspect of parameterized types that prevents this from — formally — happening?

There's no such thing as "the value IO ()". The type IO () has tons of possible values, which all have different internal representations: 1) The IO action that does nothing. 2) The IO action that prints the string "Hello World" and then returns nothing. 3) The IO action that reads a string from the console, reverses it, prints it back, and then returns nothing. 4) ... It's a common misconception to think that IO X i…

Thanks. I get that part. After digging a bit, it seems the implementation of IO is in fact a bit magical [1] (and not possible to implement in pure Haskell). GHC's IO is implemented using a GHC-internal pseudo-state monad that uses a magical "world" value to enforce linear ordering — all of which, I'm guessing, would prevent the compiler from accidentally optimizing away or memoizing side-effectful functions?

Edit: I find it interesting that Haskell tutorials and books generally don't try to peel away some of Haskell's abstractions. Many texts will tell you that "IO a" represents an "action" and expect you to take this at face value, intead of explaining how this is distinguishable from a mere function, and how it's implemented internally. For example, I would say one of the big eureka moments for any Haskell learner is to realize how lazy-evaluated graphs of functions can turn into linearly-ordered programs thanks to the transparent "baton passing" of monad chaining (IO in particular). But I've yet to find a text which articulates this idea very well.

[1] http://stackoverflow.com/a/9244715/632555

Re: Effectful Haskell: IO, Monads, Functors

#66

Earlier quoted context omitted.

There's no such thing as "the value IO ()". The type IO () has tons of possible values, which all have different internal representations: 1) The IO action that does nothing. 2) The IO action that prints the string "Hello World" and then returns nothing. 3) The IO action that reads a string from the console, reverses it, prints it back, and then returns nothing. 4) ... It's a common misconception to think that IO X i…

Thanks. I get that part. After digging a bit, it seems the implementation of IO is in fact a bit magical [1] (and not possible to implement in pure Haskell). GHC's IO is implemented using a GHC-internal pseudo-state monad that uses a magical "world" value to enforce linear ordering — all of which, I'm guessing, would prevent the compiler from accidentally optimizing away or memoizing side-effectful functions? Edit: I…

GHC's implementation of IO isn't the only one possible. For example, if Haskell had only two available IO operations (read a character or print a character), the following would be a workable definition of the IO type:

    data IO a = PutChar (Char, IO a)
              | GetChar (Char -> IO a)
              | Return a
Programs can work by constructing a value of type IO () and defining it as main, just like in regular Haskell. Note that IO is no longer a wrapped impure function, and there are no magical tokens in sight. In fact, the above implementation doesn't even have to be opaque, it can be completely exposed to programmers. Nevertheless, it's just as easy for the runtime to interpret in a sequential way, and there won't be any problems with accidentally optimizing away or memoizing anything.
Post reply on HN