3 ms·
> I realized that there is no way to take a value out of an IO purely, that is, you cannot have a function IO a -> a. It is not because IO is a Monad and Monad
by dllthomas 6y ago
> I realized that there is no way to take a value out of an IO purely, that is, you cannot have a function IO a -> a. It is not because IO is a Monad and Monads are special cased magic, but simply because the constructor of IO is not exported out of its module.
That's not really why. Even if you could unwrap the IO constructor, there is no `a` inside it!
As it turns out, with you can unwrap the IO constructor if you import GHC.Types from ghc-prim:
Prelude> import GHC.Types
Prelude GHC.Types> :set -XUnboxedTuples
Prelude GHC.Types> let (IO f) = getChar
Prelude GHC.Types> :t f
f :: GHC.Prim.State# GHC.Prim.RealWorld
-> (# GHC.Prim.State# GHC.Prim.RealWorld, Char #)
What's inside is a function. You can't get the return value out of a function without running it. You can, of course, run it. `unsafePerformIO` will give you a thunk that, when forced, will do just that.
- dwohnitmok 6y agoWhile true (and perhaps is another minor enlightenment, that the parameters of a type signature do not need to actually correspond to any runtime values, which the article talks about in its own way later), I do think this is an extremely important point that I appreciate the article bringing up and want to hammer home myself. I often see the sentiment on HN and other programming forums that the "magic of Haskell's mysterious and arcane type system with the bogeyman of inscrutable category theory and monads" (to exaggerate just a tad) prevents you from mixing pure and non-pure code in Haskell. That's not what's going on. It's just a clever choice by the standard library to not export certain functions that unwrap a type (or at least hide it behind a function with the word unsafe in it), a trick that is used by any language that has support for private and public functions. Indeed this exact same thing can be replicated by any language that is expression-focused and has support for immutable bindings. Just modify your standard library to wrap everything side-effectful in an IO type and don't let users unwrap that.
- dllthomas 6y agoOn reflection, I think that's a really good perspective. If you'll notice, my ultimate conclusion was "actually, you sort of can" so it makes a lot of sense to look at it as "you can't because that's not the interface they chose to expose." There are important reasons why that's what they chose, but that's a separate question. (Both of these questions are valid interpretations of the original wording of the question, but they are interesting considered separately.)