> The default basis library does not attempt to segregate effects into a monad, but it is perfectly straightforward to do this yourself, by providing your own layer over the standard basis
This comment is somewhat silly. The whole notion of segregating effects is about what a programmer cannot do, and not about what they can.
In ML, maybe you can segregate effects, but it is of limited value, as all of the code you use might be hiding procedural effects behind the seemingly innocent types.
In Haskell, when I read an API, I know no effects lurk behind the pure type signatures.
unsafePerformIO is not nearly as useful for introducing effects as one might think it would be. Because Haskell is lazy, any effects you might want would have to be grounded in the toplevel IO monad anyway.
To answer you directly, I've read code in various Haskell packages, everything from mime to snap, and AFAICT, it is typically used when you need to call a C library. Suppose you need to use OpenSSL's digest algorithms, for example. Because we technically don't know whether or not that library will have effects, we have to use unsafePerformIO.
Actually you can interpret unsafePerformIO as: "Dear compiler, I know you can't prove that it's safe to call this function here in a lazy setting. Please trust me that it is. Sincerely, the programmer."
Precisely. The burden is on the programmer to provide the evidence that the conditions for calling `unsafePerformIO` are satisfied. (That is, that any side effects are hidden).
Most languages have `unsafePerformIO` on every single computation :-)
Exactly -- this is an important point Harper misses here. In SML, you can have functions of type "unit -> unit" and have no idea what the heck it is doing. The language allows effects, and so it could be doing anything. Even a function "int -> int" or whatever could be doing nasty effects behind the scenes. You just can't tell. (Style dictates that non-benign effects in the last case are to be fiercely avoided, but the former case comes up a lot.)
In Haskell, all the effects are encoded into the type. You might have 200 different monad transformers -- maybe monad, list monad, state monad, etc etc all layered on top of each other -- but the information is there if you want to decode what the type means. Effects can sneak in with unsafePerformIO, but this is also considered terrible style. The huge types you can get from all the monads and monad transformers can certainly be a problem, but there's a tradeoff to be considered here that Harper just dismisses.
Harper addresses this in a comment on the OP. You replace the whole standard library with a new, monad-tamed one. Which works, and could be interesting and useful, but with similar logic you could say things like "Of course Lisp has static types!" which I can't imagine him promoting.
SML is impure. You are arguing for purity, which is a separate issue from support for monads. Saying "SML doesn't really have support for monads because it's impure" is confusing an idea (monads) with a particular application of that idea (using special monads like IO to separate pure from side-effectful code).
To be fair, Harper's comment is practically a pretty silly proposition. But the primary technical claim of his post (SML supports monads) is true. This is a common issue with Harper: he rarely says anything factually incorrect, but the moral conclusions he draws are out of proportion with the technical points he makes.
I never intended to imply that lack of effect segregation imposition means that there can be no Monads in SML, I was just replying to that particular tidbit of his.
While there may very well be monads in SML, he still haven't made it clear the Monad abstraction is possible, because he hasn't actually written any code that's generalized to any monad (e.g: Haskell's sequence, liftM2, and so forth).
That last part is trivial: Just put those generic function in a functor, that take a Monad module as a parameter. Assuming I got the functor syntax right (unlikely), it should look something like:
functor MonadUtils MONAD = struct
sequence : 'a MONAD.monad list -> 'a list MONAD.monad
(* and so on *)
end
functor MonadUtils (M : MONAD) = struct
(* type annotations not strictly necessary *)
fun sequence (ms : 'a M.monad list) : 'a list M.monad =
let fun mcons (elt, rest) = M.bnd elt (fn e => M.bnd rest (fn r => M.ret (e::r)))
in foldr mcons (M.ret []) ms
end
end
You could analogously explain how of course Lisp has types, you just implement Hindley-Milner and slot it in in front of the standard library. Only, if the author wrote that, I'd think I was hallucinating.
16 comments
[ 3.4 ms ] story [ 43.3 ms ] threadThis comment is somewhat silly. The whole notion of segregating effects is about what a programmer cannot do, and not about what they can.
In ML, maybe you can segregate effects, but it is of limited value, as all of the code you use might be hiding procedural effects behind the seemingly innocent types.
In Haskell, when I read an API, I know no effects lurk behind the pure type signatures.
I wonder how often unsafePerformIO : IO 'a -> 'a is employed
To answer you directly, I've read code in various Haskell packages, everything from mime to snap, and AFAICT, it is typically used when you need to call a C library. Suppose you need to use OpenSSL's digest algorithms, for example. Because we technically don't know whether or not that library will have effects, we have to use unsafePerformIO.
Most languages have `unsafePerformIO` on every single computation :-)
In Haskell, all the effects are encoded into the type. You might have 200 different monad transformers -- maybe monad, list monad, state monad, etc etc all layered on top of each other -- but the information is there if you want to decode what the type means. Effects can sneak in with unsafePerformIO, but this is also considered terrible style. The huge types you can get from all the monads and monad transformers can certainly be a problem, but there's a tradeoff to be considered here that Harper just dismisses.
To be fair, Harper's comment is practically a pretty silly proposition. But the primary technical claim of his post (SML supports monads) is true. This is a common issue with Harper: he rarely says anything factually incorrect, but the moral conclusions he draws are out of proportion with the technical points he makes.
While there may very well be monads in SML, he still haven't made it clear the Monad abstraction is possible, because he hasn't actually written any code that's generalized to any monad (e.g: Haskell's sequence, liftM2, and so forth).