Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> 1. a description of how to carry out I/O that is performed by a separate system

> 2. does the I/O inside the function before returning

How do you distinguish those two things? To put my cards on the table, I believe Haskell does 2, and I think my Haskell effect system Bluefin makes this abundantly clear. (Zig's `Io` seems to correspond to Bluefin's `IOE`.)

There is a persistent myth in the Haskell world (and beyond) that Haskell does 1. In fact I think it's hard to make it a true meaningful statement, but I can probably just about concede it is with a lot of leeway on what it means for I/O to be "performed by a separate system", and even then only in a way that it's also true and meaningful for every other language with a run time system (which is basically all of them).

The need to believe that Haskell does 1 comes from the insistence that Haskell be considered a "pure" language, and the inference that means it doesn't do I/O, and therefore the need that "something else" must do I/O. I just prefer not to call Haskell a "pure" language. Instead I call it "referentially transparent", and the problem vanishes. In Haskell program like

    main :: IO ()
    main = do
       foo
       foo

    foo :: IO ()
    foo = putStrLn "Hello"
I would say that "I/O is done inside `foo` before returning". Simple. No mysteries or contradiction.

https://hackage-content.haskell.org/package/bluefin/docs/Blu...





> I would say that "I/O is done inside `foo` before returning".

It is not. The documentation and the type very clearly shows this:

https://hackage.haskell.org/package/base-4.21.0.0/docs/Prelu...

> A value of type `IO a` is a computation which, when performed, does some I/O before returning a value of type a.

So your function foo does no IO in itself. It returns a "computation" for main to perform. And only main can do this, since the runtime calls main. You can call foo as much as you like, but nothing will be printed until you bind any of the returned IO values.

Comparing it to other languages is a bit misleading since Haskell is lazy. putStrLn isn't even evaluated until the IO value is needed. So even "before returning" is wrong no matter how you choose to define "inside".


I'm also pretty sure that its immaterial if Haskell does 1 or not. This is an implementation detail and not at all important to something being a Monad or not.

My understanding is requiring 1 essentially forces you to think of every Monad as being free.


Ah! My favourite Haskell discussion. So, consider these two programs, the first in Haskell:

    main :: IO ()
    main = do
      foo
      foo

    foo :: IO ()
    foo = putStrLn "Hello"
and the second in Python:

    def main():
      foo()
      foo()

    def foo():
      print("Hello")
For the Python one I'd say "I/O is done inside `foo` before returning". Would you? If not, why not? And if so, what purpose does it serve to not say the same for the Haskell?

My Haskell is rusty enough that I don’t know the proper syntax for it, but you can make a program that calls foo and then throws away / never uses the IO computation. Because Haskell is lazy, “Hello” will never be printed.

You can do this

    main = do
      let x = foo
      putStrLn "foo was never executed"
but you can also do this

    def main():
      x = foo
      print("foo was never executed")
What's the difference?



Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: