I'm reading about the math foundation behind Haskell - I've learned about how closures can be used to save state in a function.
I was wondering if Haskell allows closures, and how they work because they are not pure functions?
If a function modifies it's closed-over state it will be capable of giving different outputs on identical inputs.
How is this not a problem in Haskell? Is it because you can't reassign a variable after you initially assign it a value?
You actually can simulate closures in Haskell, but not the way you might think. First, I will define a closure type:
data Closure i o = Respond (i -> (o, Closure i o ))
This defines a type that at each "step" takes a value of type i which is used to compute a response of type o.
So, let's define a "closure" that accepts empty inputs and answers with integers, i.e.:
incrementer :: Closure () Int
This closure's behavior will vary from request to request. I'll keep it simple and make it so that it responds with 0 to the first response and then increments its response for each successive request:
incrementer = go 0 where
go n = Respond $ \() -> (n, go (n + 1))
We can then repeatedly query the closure, which yields a result and a new closure:
query :: i -> Closure i o -> (o, Closure i o)
query i (Respond f) = f i
Notice that the second half of the above type resembles a common pattern in Haskell, which is the State monad:
newtype State s a = State { runState :: s -> (a, s) }
It can be imported from Control.Monad.State. So we can wrap query in this State monad:
query :: i -> State (Closure i o) o
query i = state $ \(Respond f) -> f i
... and now we have a generic way to query any closure using the State monad:
someQuery :: State (Closure () Int) (Int, Int)
someQuery = do
n1 <- query ()
n2 <- query ()
return (n1, n2)
Let's pass it our closure and see what happens:
>>> evalState someQuery incrementer
(0, 1)
Let's write a different closure that returns some arbitrary pattern:
weirdClosure :: Closure () Int
weirdClosure = Respond (\() -> (42, Respond (\() -> (666, weirdClosure))))
... and test it:
>>> evalState someQuery weirdClosure
(42, 666)
Now, writing closures by hand seems pretty awkward. Wouldn't it be nice if we could use do notation to write the closure? Well, we can! We only have to make one change to our closure type:
data Closure i o r = Done r | Respond (i -> (o, Closure i o r))
Now we can define a Monad instance (from Control.Monad) for Closure i o:
instance Monad (Closure i o) where
return = Done
(Done r) >>= f = f r
(Respond k) >>= f = Respond $ \i -> let (o, c) = k i in (o, c >>= f)
And we can write a convenience function which corresponds to servicing a single request:
answer :: (i -> o) -> Closure i o ()
answer f = Respond $ \i -> (f i, Done ())
... which we can use to rewrite all our old closures:
incrementer :: Closure () Int ()
incrementer = forM_ [1..] $ \n -> answer (\() -> n)
weirdClosure :: Closure () Int r
weirdClosure = forever $ do
answer (\() -> 42)
answer (\() -> 666)
Now we just change our query function to:
query :: i -> StateT (Closure i o r) (Either r) o
query i = StateT $ \x -> case x of
Respond f -> Right (f i)
Done r -> Left r
... and use it to write queries:
someQuery :: StateT (Closure () Int ()) (Either ()) (Int, Int)
someQuery = do
n1 <- query ()
n2 <- query ()
return (n1, n2)
Now test it!
>>> evalStateT someQuery incrementer
Right (1, 2)
>>> evalStateT someQuery weirdClosure
Right (42, 666)
>>> evalStateT someQuery (return ())
Left ()
However, I still don't consider that a truly elegant approach, so I'm going to conclude by shamelessly plugging my Proxy type in my pipes as a much general and more structured way of writing closures and their consumers. The Server type represents a generalized closure and the Client represents a generalized consumer of a closure.
The closure just 'adds' additional variables to function, so there is nothing more you can do with them than you can with 'normal' ones, that is, certainly not modify the state.
Read more:
Closures (in Haskell)
As others have said, Haskell does not allow the "state" in a closure to be altered. This prevents you from doing anything that might break function purity.
Related
type InterpreterMonad = StateT (Env, Env) (ErrorT String IO ) ()
interpreter :: Stmts -> InterpreterMonad
interpreter (Statements s EmptyStmts) = interpreteStmt s
interpreter (Statements s stmts) = interpreteStmt s >>= \m -> (interpreter stmts)
-- definicja funkcji
interpreteStmt :: Stmt -> InterpreterMonad
interpreteStmt (DefFun (VarName name) args typ begin statements end) = get >>=
\(envOut, (sInnerEnvFun, sInnerEnvEVal)) -> case (Map.lookup (VarName name) sInnerEnvFun) of
Nothing -> put ( ( envOut, ((Map.insert (VarName name) (DefFun (VarName name) args typ begin statements end) sInnerEnvFun), sInnerEnvEVal)) ) >>= \_ -> return ()
(Just x) -> throwError "ee"
Hi, I cannot understand why get and put functions can be called? How the Haskell know "where" is state? I have a problem in favour of imperative programming- after all, such function we have to call on object ( as method) or pass state by argument.
When you use the State monad functions get and put and sequence them with do notation or >>=, you are constructing a "recipe" (often called an "action") for accessing and modifying local state that will eventually be used with some particular state. For example:
increment = do
count <- get
put (count + 1)
This action can then be used with the part of the State interface that lets you run actions by passing in a state. We will use execState, which returns the state and discards the other value, to demonstrate:
> execState increment 1
2
execState is what connects the action increment with the state it modifies. increment by itself doesn't contain any state, it is just a recipe for interacting with state that you must evaluate later.
The State type (which StateT is a generalization of) is implemented something like this:
newtype State s a =
State { -- A function that takes a state of type `s` and
-- produces a pair `(a, s)` of a result of type
-- `a` and a new state of type `s`.
runState :: s -> (a, s)
}
This type has instances for Functor, Applicative and Monad that allow you to assemble complex State values out of simpler ones:
-- I won't write the code for these here
instance Functor (State s) where ...
instance Applicative (State s) where ...
instance Monad (State s) where ...
Basically, State is a shortcut for chaining functions of types that look like s -> (a, s), but without having to pass around those s values explicitly. When you're ready to actually "feed" a State action with an s value you use one of these operations (depending on which part of the result you want):
runState :: State s a -> s -> (a, s)
evalState :: State s a -> s -> a
execState :: State s a -> s -> s
What put and get do, then, is interact with the "hidden" state argument that the State type implicitly passes around in your code. Their implementations are something like this:
get :: State s s
get = State (\s -> (s, s))
put :: s -> State s ()
put s = State (\_ -> ((), s))
And that's it!
I'm trying to work out if it's possible to write an abstraction for the following situation. Suppose I have a type a with function a -> m Bool e.g. MVar Bool and readMVar. To abstract this concept out I create a newtype wrapper for the type and its function:
newtype MPredicate m a = MPredicate (a,a -> m Bool)
I can define a fairly simple operation like so:
doUnless :: (Monad m) => Predicate m a -> m () -> m ()
doUnless (MPredicate (a,mg)) g = mg a >>= \b -> unless b g
main = do
b <- newMVar False
let mpred = MPredicate (b,readMVar)
doUnless mpred (print "foo")
In this case doUnless would print "foo". Aside: I'm not sure whether a type class might be more appropriate to use instead of a newtype.
Now take the code below, which outputs an incrementing number then waits a second and repeats. It does this until it receives a "turn off" instruction via the MVar.
foobar :: MVar Bool -> IO ()
foobar mvb = foobar' 0
where
foobar' :: Int -> IO ()
foobar' x = readMVar mvb >>= \b -> unless b $ do
let x' = x + 1
print x'
threadDelay 1000000
foobar' x'
goTillEnter :: MVar Bool -> IO ()
goTillEnter mv = do
_ <- getLine
_ <- takeMVar mv
putMVar mv True
main = do
mvb <- newMVar False
forkIO $ foobar mvb
goTillEnter mvb
Is it possible to refactor foobar so that it uses MPredicate and doUnless?
Ignoring the actual implementation of foobar' I can think of a simplistic way of doing something similar:
cycleUnless :: x -> (x -> x) -> MPredicate m a -> m ()
cycleUnless x g mp = let g' x' = doUnless mp (g' $ g x')
in g' $ g x
Aside: I feel like fix could be used to make the above neater, though I still have trouble working out how to use it
But cycleUnless won't work on foobar because the type of foobar' is actually Int -> IO () (from the use of print x').
I'd also like to take this abstraction further, so that it can work threading around a Monad. With stateful Monads it becomes even harder. E.g.
-- EDIT: Updated the below to show an example of how the code is used
{- ^^ some parent function which has the MVar ^^ -}
cycleST :: (forall s. ST s (STArray s Int Int)) -> IO ()
cycleST sta = readMVar mvb >>= \b -> unless b $ do
n <- readMVar someMVar
i <- readMVar someOtherMVar
let sta' = do
arr <- sta
x <- readArray arr n
writeArray arr n (x + i)
return arr
y = runSTArray sta'
print y
cycleST sta'
I have something similar to the above working with RankNTypes. Now there's the additional problem of trying to thread through the existential s, which is not likely to type check if threaded around through an abstraction the likes of cycleUnless.
Additionally, this is simplified to make the question easier to answer. I also use a set of semaphores built from MVar [MVar ()] similar to the skip channel example in the MVar module. If I can solve the above problem I plan to generalize the semaphores as well.
Ultimately this isn't some blocking problem. I have 3 components of the application operating in a cycle off the same MVar Bool but doing fairly different asynchronous tasks. In each one I have written a custom function that performs the appropriate cycle.
I'm trying to learn the "don't write large programs" approach. What I'd like to do is refactor chunks of code into their own mini libraries so that I'm not building a large program but assembling lots of small ones. But so far this particular abstraction is escaping me.
Any thoughts on how I might go about this are very much appreciated!
You want to cleanly combine a stateful action having side effects, a delay, and an independent stopping condition.
The iterative monad transformer from the free package can be useful in these cases.
This monad transformer lets you describe a (possibly nonending) computation as a series of discrete steps. And what's better, it let's you interleave "stepped" computations using mplus. The combined computation stops when any of the individual computations stops.
Some preliminary imports:
import Data.Bool
import Control.Monad
import Control.Monad.Trans
import Control.Monad.Trans.Iter (delay,untilJust,IterT,retract,cutoff)
import Control.Concurrent
Your foobar function could be understood as a "sum" of three things:
A computation that does nothing but reading from the MVar at each step, and finishes when the Mvar is True.
untilTrue :: (MonadIO m) => MVar Bool -> IterT m ()
untilTrue = untilJust . liftM guard . liftIO . readMVar
An infinite computation that takes a delay at each step.
delays :: (MonadIO m) => Int -> IterT m a
delays = forever . delay . liftIO . threadDelay
An infinite computation that prints an increasing series of numbers.
foobar' :: (MonadIO m) => Int -> IterT m a
foobar' x = do
let x' = x + 1
liftIO (print x')
delay (foobar' x')
With this in place, we can write foobar as:
foobar :: (MonadIO m) => MVar Bool -> m ()
foobar v = retract (delays 1000000 `mplus` untilTrue v `mplus` foobar' 0)
The neat thing about this is that you can change or remove the "stopping condition" and the delay very easily.
Some clarifications:
The delay function is not a delay in IO, it just tells the iterative monad transformer to "put the argument in a separate step".
retract brings you back from the iterative monad transformer to the base monad. It's like saying "I don't care about the steps, just run the computation". You can combine retract with cutoff if you want to limit the maximum number of iterations.
untilJustconverts a value m (Maybe a) of the base monad into a IterT m a by retrying in each step until a Just is returned. Of course, this risks non-termination!
MPredicate is rather superfluous here; m Bool can be used instead. The monad-loops package contains plenty of control structures with m Bool conditions. whileM_ in particular is applicable here, although we need to include a State monad for the Int that we're threading around:
import Control.Monad.State
import Control.Monad.Loops
import Control.Applicative
foobar :: MVar Bool -> IO ()
foobar mvb = (`evalStateT` (0 :: Int)) $
whileM_ (not <$> lift (readMVar mvb)) $ do
modify (+1)
lift . print =<< get
lift $ threadDelay 1000000
Alternatively, we can use a monadic version of unless. For some reason monad-loops doesn't export such a function, so let's write it:
unlessM :: Monad m => m Bool -> m () -> m ()
unlessM mb action = do
b <- mb
unless b action
It's somewhat more convenient and more modular in a monadic setting, since we can always go from a pure Bool to m Bool, but not vice versa.
foobar :: MVar Bool -> IO ()
foobar mvb = go 0
where
go :: Int -> IO ()
go x = unlessM (readMVar mvb) $ do
let x' = x + 1
print x'
threadDelay 1000000
go x'
You mentioned fix; sometimes people indeed use it for ad-hoc monadic loops, for example:
printUntil0 :: IO ()
printUntil0 =
putStrLn "hello"
fix $ \loop -> do
n <- fmap read getLine :: IO Int
print n
when (n /= 0) loop
putStrLn "bye"
With some juggling it's possible to use fix with multi-argument functions. In the case of foobar:
foobar :: MVar Bool -> IO ()
foobar mvb = ($(0 :: Int)) $ fix $ \loop x -> do
unlessM (readMVar mvb) $ do
let x' = x + 1
print x'
threadDelay 1000000
loop x'
I'm not sure what's your MPredicate is doing.
First, instead of newtyping a tuple, it's probably better to use a normal algebric data type
data MPredicate a m = MPredicate a (a -> m Bool)
Second, the way you use it, MPredicate is equivalent to m Bool.
Haskell is lazzy, therefore there is no need to pass, a function and it's argument (even though
it's usefull with strict languages). Just pass the result, and the function will be called when needed.
I mean, instead of passing (x, f) around, just pass f x
Of course, if you are not trying to delay the evaluation and really need at some point, the argument or the function as well as the result, a tuple is fine.
Anyway, in the case your MPredicate is only there to delay the function evaluation, MPredicat reduces to m Bool and doUnless to unless.
Your first example is strictly equivalent :
main = do
b <- newMVar False
unless (readMVar b) (print "foo")
Now, if you want to loop a monad until a condition is reach (or equivalent) you should have a look at the monad-loop package. What you are looking it at is probably untilM_ or equivalent.
I'm having this snippet, could you explain me what is does, especially >>= ?
instance Monad (State s) where
return a = State $ \s -> (a, s)
State st >>= f = State $ \s ->
let (a, s') = st s
in runState (f a) s'
Eventually could you write it in a more clear form?
Thanks
This code defines what >>= is supposed to do.
State func1 >>= f =
State $ ...stuff...
The State constructor has to be followed by a function that takes the current state and returns a new state and a result. So ...stuff... must be such a function, and func1 must also be such.
State $ \ state1 -> ...
OK, so, we want to "run" func1 by giving it some state:
let (resultA, state2) = func1 state1
Next, we want to call f to get the next monadic action to do:
State func2 = f resultA
and now we "run" that, feeding it state2 rather than state1:
in func2 state2
The example code above uses runState, but I thought I'd spell it out explicitly for clarity.
State func1 >>= f =
State $ state1 ->
let (resultA, state2) = func1 state1
State func2 = f resultA
in func2 state2
If you squint, you can see that State really is a wrapper for functionality related to functions of type (a,s)->(b,s), only curried: a -> s -> (b,s).
Chaining functions of type (a,s)->(b,s) is really easy - plain composition. That way they can pass both "state" (s) and computation "results" (a to produce b) along the invocation chain. Of course, it becomes obvious that the distinction between the "state" and "results" is arbitrary. But if you are dealing with the curried version, it becomes a little more complicated, and that's taken care of by >>=.
But what is Monad for, if we can "chain really easily" the functions of type before currying? The Monad is there to take care of boilerplate related to state propagation even when the function doesn't depend on it - because most of functions don't. They start depending on state and changing the state (well, they actually become stateful), when they access the second projection of the tuple using get and put. Without State monad that would look like so:
get :: (a,s) -> (s,s)
get (_,s) = (s,s)
put :: (s,s) -> ((),s)
put (s,_) = ((),s)
By currying (a,s)->(b,s), we get a hygienic stateful computation, separating concerns for dependency on a and s: we get rid of explicit dependency on s, unless specifically called out by the use of get and put. It's important to keep in mind their existence - otherwise it is not entirely obvious what's the fuss with passing s around:
get = State $ \s -> (s,s)
put s = State $ \_ -> ((),s)
Now, back to >>=. What this function does, is combines s->(a,s) and a->s->(b,s) to obtain s->(b,s) - which now can be used to combine with >>= some more. If we didn't have the State constructor wrapper, the binding would look like:
as >>= f = \s -> (uncurry f) $ as s
That is, given state, feed into as to produce (a,s), which are fed into f (uncurried to get (a,s)->(b,s) again) to produce (b,s). But since we have the State wrapper, it becomes a little less obvious:
(State as) >>= f = State $ \s -> let (a,s') = as s --mean to call f (a,s'), but
(State g) = f a --need to remove the wrapper
in g s' --to pass s' to (s->(b,s))
The unwrapping in your example is done using runState.
Imagine a stateful function:
type StatefulFunction s a b = s -> a -> (b, s)
Let's create two such functions:
a :: StatefulFunction Int Int Int
a s x = (x + s, s)
b :: StatefulFunction Int Int Int
b s x = (x + s, s+1)
Those functions can change the behaviour based on the explicit state parameter. However, chaining them is tedious:
let startState = 0
let aParam = 42
let (aResult, newState) = a startState x
let bParam = 99
let (bResult, newState') = b newState y
State monad makes this chaining easier, and what I wrote above is precisely what >>= is doing for it:
a :: Int -> State Int Int
a x = fmap (+x) get
b :: Int -> State Int Int
b x = do
s <- get
put $ s + 1
return $ s + x
foo :: State Int Int
foo = (a aParam) >>= (b bParam)
I am working on designing a Haskell module that is designed solve a math problem, which may have various parameterizations. The module exports a function:
run_and_output_parameterization :: ProblemParams -> String -> IO ()
where the idea is that ProblemParams objects will be generated in some "controller" and called as follows:
map (\(pp, name) -> run_and_output_parameterization pp name) (zip pp_list names_list)
My question is, within the module, there are some functions such as indexing functions which I would like to partially apply for a particular parameterization. For example,
evenly_spaced_point_approx :: Int -> Int -> Int -> Double -> Double -> Int
evenly_spaced_point_approx xmin xmax xstep_i xstep_d target = pt
where
pt = max (min (round (target/xstep_d) * xstep_i) xmax) xmin
evenly_spaced_si_approx target = evenly_spaced_point_approx (_pp_si_min pp) (_pp_si_max pp) (_pp_nstep_s pp) (_pp_nstep_sd pp) target
evenly_spaced_wi_approx target = evenly_spaced_point_approx (_pp_wi_min pp) (_pp_wi_max pp) (_pp_nstep_w pp) (_pp_nstep_wd pp) target
I would like to use the functions evenly_spaced_si_approx and evenly_spaced_wi_approx within the module for a particular ProblemParameter data structure (called pp).
Is there a way I can tell Haskell to partially apply all dependent functions, or is this something I have to do by hand? Also, my apologies for being imprecise with the functional programming terminology.
If you have a lot of functions that need the same parameter, and that's the only (or last) parameter they take then you can take advantage of the Monad instance for (->) r. Alternatively, you can wrap everything in the Reader monad, whose definition is basically
newtype Reader r a = Reader { runReader :: r -> a }
instance Monad (Reader r) where
return a = Reader $ \_ -> a
m >>= f = Reader $ \r -> runReader (f (runReader m r)) r
Which, compared to the Monad instance for (->) r:
instance Monad ((->) r) where
return a = const a
m >>= f = \r -> f (m r) r
How can you use this? For example, if you had a single parameter pp :: ProblemParams, then you could write functions as
-- Some declarations
smallFunc1 :: ProblemParams -> Double
smallFunc2 :: ProblemParams -> Double
smallFunc3 :: Int -> ProblemParams -> Double
doStuff :: ProblemParams -> Double -- Just a random return type
doStuff = do -- Keep the parameter implicit
result1 <- smallFunc1 -- The ProblemParams are automatically passed
result2 <- smallFunc2
result3 <- smallFunc3 10
return $ result1 + result2 + result3
And this works quite well, you just have to make sure that all of smallFunc1, smallFunc2, and smallFunc3 10 take a ProblemParams as their last parameter (notice the inclusion of 10 with smallFunc3). The Monad instance for functions will pass that parameter implicitly in all the binds. Think of it as returning a value before that value has been computed. You get to bind the "future" return value of smallFunc1 to result1.
Alternatively, you could use the Reader monad:
type Problem a = Reader ProblemParams a
reader :: (r -> a) -> Reader r a
reader f = do
r <- ask
return $ f r
-- reader f = ask >>= return . f
smallFunc1' :: Problem Double
smallFunc1' = reader smallFunc1
smallFunc2' :: Problem Double
smallFunc2' = reader smallFunc2
smallFunc3' :: Int -> Problem Double
smallFunc3' i = reader (smallFunc3 i)
doStuff :: ProblemParams -> Double
doStuff pp = flip runReader pp $ do
result1 <- smallFunc1'
result2 <- smallFunc2'
result3 <- smallFunc3' 10
return $ result1 + result2 + result3
The reason why we have to create a reader function that lifts our primitives to the Reader monad is that Reader is actually defined in terms of the transformer ReaderT as
type Reader r a = ReaderT r Identity a
around the Identity monad.
Whichever you decide to use is up to you. I think most people would be more familiar with the Reader version, and if you decided to stack on some more transformers later it'd be really simple. The Reader monad basically helps to make the function signatures look monadic, since ProblemParams -> Double doesn't look like a normal monad signature. It will use a bit more code, but it may be that it helps you reason about your program.
Note: I haven't run any of this code, so be warned that small errors may exist. If anyone spots a problem, just let me know and I'll fix it.
An example with the Par monad and ReaderT:
type App a = ReaderT ProblemParams Par a
runApp :: ProblemParams -> App a -> a
runApp pp app = runPar $ runReaderT app pp
Then you can simply use lift to raise Par actions to App actions:
parReader :: (ProblemParams -> Par a) -> App a
parReader f = do
r <- ask
lift $ f r
-- parReader f = ask >>= lift . f
doStuff :: ProblemParams -> Double
doStuff pp = runApp pp $ do
result1 <- parReader parAction1
result2 <- parReader parAction2
result3 <- parReader (parAction3 10)
return $ result1 + result2 + result3
I'm about 99% sure that the monad stack will not affect your parallelism at all, since the Reader monad executes first, essentially applying your ProblemParams to all the functions, then it runs the Par action.
If I understood correctly, a monad is just the implementation of a bind >>= and a return operator following certain rules which basically compose 2 functions of different return types together. So, for example, those are equivalent:
putStrLn "What is your name?"
>>= (\_ -> getLine)
>>= (\name -> putStrLn ("Welcome, " ++ name ++ "!"))
(bind (putStrLn "What is your name?")
(bind
(\_ -> getLine)
(\name -> putStrLn ("Welcome, " ++ name ++ "!"))))
But if we strongly normalize this expression, the final result will be just:
(putStrLn ("Welcome, " ++ getline ++ "!"))
The first statement (putStrLn "What is your name?") is completely lost. Also, getLine looks like a function with no arguments, which is nonsense. So how does this work, and what is the actual definition of the >>= and return functions?
Your logical misstep is that you assume certain reduction rules hold which do not. In particular, you appear to be using
f >>= (\x -> g x) ==== g f
If that held then, yes, monads would be pretty silly: (>>=) would just be flip ($). But it doesn't, in general, hold at all. In fact, the very reason it doesn't hold is what provides monads an opportunity to be interesting.
For a little bit of further exploration, here's the one monad where (>>=) == flip ($) (basically) holds.
newtype Identity a = Identity { unIdentity :: a }
To make our equations work out, we'll have to use that Identity a ~ a. This isn't strictly true, obviously, but let's pretend. In particular, Identity . unIdentity and unIdentity . Identity are both identities, no-ops, and we can freely apply Identity or unIdentity however we like to make types match
instance Functor Identity where
fmap f (Identity a) = Identity (f a)
instance Monad Identity where
return a = Identity a -- this is a no-op
ida >>= f = f (unIdentity ida)
Now, in particular, we want to examine
ida :: Identity a
f :: a -> b
ida >>= Identity . f :: Identity b
===
Identity (f (unIdentity ida)) :: Identity b
and if we throw away the Identity/unIdentity noise and thus produce the knowledge that ida = Identity a for some a
Identity (f (unIdentity ida)) :: Identity b
===
Identity (f a) :: Identity b
=== ~
f a :: b
So, while (>>=) == flip ($) forms a certain basis of intuition about (>>=)... in any circumstance more interesting than the Identity monad (and all other monads are) it doesn't hold exactly.
Seems to be a misunderstanding of how evaluation in IO proceeds in Haskell. If you look at the type signature for (>>=):
λ: :t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b
It takes a monadic value parameterized by a type a, and a function which accepts a type of the same type and applies it inside the function body yielding a monadic value of type b.
The IO monad itself is a rather degenerate monad since it has special status in Haskell's implementation. A type of IO a stands for a potentially impure computation which, when performed, does some IO before returning a value of type a.
The first statement (putStrLn "What is your name?") is completely
lost.
The misunderstanding about this statement is that the value of putStrLn :: String -> IO () does in fact lose it's value in some sense, or more precisely it just yields the unit type () to the bound function after performing the IO action of printing a string to the outside world.
But if we strongly normalize this expression, the final result will be
just: (putStrLn ("Welcome, " ++ getline ++ "!"))
It's best to think of getLine :: IO String as being a computation yielding a value instead of a value itself. In this case as well the function getLine is not itself substituted in but the result of the computation it performs is, which behaves like you expect it to: getting a value from stdin and printing it back out.
It has been so long til I asked that question! The simple answer is that, no, the term I posted does not reduce to putStrLn ("Welcome, " ++ getline ++ "!"). Instead, its normal form will have the shape bind foo (\ _ -> bind bar (\ _ -> ...)), i.e., a chain of lambdas, which holds the ordering information I was worried about.
[...] what are the actual definitions for the (>>=) and return functions?
From section 6.1.7 (page 75) of the Haskell 2010 report:
The IO type serves as a tag for operations (actions) that interact with the outside world. The IO type is abstract: no constructors are visible to the user. IO is an instance of the Monad and Functor classes.
the crucial point being:
The IO type is abstract: no constructors are visible to the user.
There are no actual (written in idiomatic Haskell) definitions - it's the implementors' choice as to which model to use: state-threading, continuations, direct effects, etc. (This wasn't always the case - I provide more details here :-) We also benefit, as we're able to choose the most convenient model for the investigation being made.
So how does this work [...]?
I will choose the direct-effect model, based on examples from Philip Wadler's How to Declare an Imperative:
(* page 26, modified *)
type 'a io = oi -> 'a
infix >>=
val >>= : 'a io * ('a -> 'b io) -> 'b io
fun m >>= k = fn Oblige => let
val x = m Oblige
val y = k x Oblige
in
y
end
val return : 'a -> 'a io
fun return x = fn Oblige => x
val putc : char -> unit io
fun putc c = fn Oblige => putcML c
val getc : char io
val getc = fn Oblige => getcML ()
I'm using a new type:
datatype oi = Oblige
to reserve the unit type and its value () for the usual purpose of vacuous
results, for clarity.
(Yes - that's Standard ML: just imagine it's 1997, and you're writing a
prototype Haskell implementation ;-)
With the help of some extra definitions:
val gets : (char list) io
val putsl : char list -> unit io
that Haskell code sample, modified slightly:
putStrLn "What is your name?" >>=
(\_ -> getLine >>=
(\name -> putStrLn (greet name)))
greet :: String -> String
greet name = "Welcome, " ++ name ++ "!"
translates to:
putsl "What is your name?"
>>= (fn _ => gets
>>= (fn name => putsl (greet name))
where:
val greet : char list -> char list
fun greet name = List.concat (String.explode "Welcome, "::name::[#"!"])
All going well, the sample should simplify down to:
fun Oblige => let
val x = putsl "What is your name?" Oblige
val name = gets Oblige
val y = putsl (greet name) Oblige
in
y
end
Even though x isn't used it's still evaluated in Standard ML, which causes the prompt "What is your name?" to be displayed.
Now for a guess at the next question...Standard ML and Haskell are both functional languages - could all that oi stuff be transferred across to Haskell?
I was wrong? Meh; I'll answer it anyway - sort of; you can read about what I devised over here. If that was just too abominable to contemplate...well, here are those extra Standard ML definitions:
(* from pages 25-26, verbatim *)
val putcML : char -> unit
fun putcML c = TextIO.output1(TextIO.stdOut,c);
val getcML : unit -> char
fun getcML () = valOf(TextIO.input1(TextIO.stdIn));
(* Caution: work of SML novice... *)
val gets = fn Oblige => let
val c = getc Oblige
in
if c = #"\n" then
[]
else
let
val cs = gets Oblige
in
(c::cs)
end
end
fun putsl cs = fn Oblige => let
val _ = putsl cs Oblige
val _ = putc #"\n" Oblige
in
()
end
val puts : char list -> unit io
fun puts cs = fn Oblige => case cs of
[] => ()
| (c::cs) => let val _ = putc c Oblige in
puts cs Oblige