On this monad reader example:
import Control.Monad.Reader
tom :: Reader String String
tom = do
env <- ask -- gives you the environment which in this case is a String
return (env ++ " This is Tom.")
jerry :: Reader String String
jerry = do
env <- ask
return (env ++ " This is Jerry.")
tomAndJerry :: Reader String String
tomAndJerry = do
t <- tom
j <- jerry
return (t ++ "\n" ++ j)
runJerryRun :: String
runJerryRun = (runReader tomAndJerry) "Who is this?"
we can read the ˋdoˋ notation as
tomAndJerry = tom >>= \t -> ...
where ultimately you get a String back. Ok, so, basically ˋ(runReader tomAndJerry) "Who is this?"ˋ is a reader such that ˋaskˋ gives ˋWho is this?ˋ. However, ˋtomˋ has no argument names. How is ask able to run?
Can I think of ˋtom :: Reader String Stringˋ as something like C++ generics where ˋReaderˋ is bound to some static class in compile time, which always produces "Who is this?" when you call ask on it? Because as far as I know, ˋaskˋ is something that takes a ˋReader String Stringˋ and it's not explicitly getting it anywhere as an argument.
I know that it gets the context somehow, but I'm intersted in how it happens in the language itself.
I didn't try anything as it's not a problem itself
A thing of type Reader r a
is, in reality, not a value of type a
, but a function of type r -> a
that takes an r
environment and produces a value of type a
. Check out how many of your questions go away if I make this simple substitution in your code:
-- skip all imports, so we can use our own ask implementation
ask :: String -> String
ask s = s
tom :: String -> String
tom = do
env <- ask -- gives you the environment which in this case is a String
return (env ++ " This is Tom.")
jerry :: String -> String
jerry = do
env <- ask
return (env ++ " This is Jerry.")
tomAndJerry :: String -> String
tomAndJerry = do
t <- tom
j <- jerry
return (t ++ "\n" ++ j)
Where does ask
get its environment from? Well, it's a function, and it takes the environment as a parameter, that's where! Same for tom
: it gets its environment passed to it as an argument.
Well, you may say, but tom = do {- ... -}
doesn't look like a function that takes an argument. After all, there's no argument there on the left of the =
sign!
You're right. It doesn't look like a function to a traditional imperative programmer. Nevertheless, it is. Don't worry... you'll get used to it over time! Just to give a really stupid example:
f = (+)
Although this doesn't look like a function (it doesn't have arguments to the left of the =
sign), it is a function. It is exactly the same function as (+)
is. The very similar definitions f x y = x + y
or f x y = (+) x y
, which look more like a function, mean almost exactly the same thing. A slightly less stupid example:
g = if somethingComplicated then (+) else (*)
Functions are simply objects that you can pass around, store in variables, return as the result of complicated calculations, etc. They don't always need to name their arguments to be a function!
Now let's walk through your post a bit.
Ok, so, basically
(runReader tomAndJerry) "Who is this?"
is a reader such thatask
givesWho is this?
.
I don't like this phrasing. It is just tomAndJerry
that is a reader; the fuller phrase (runReader tomAndJerry) "Who is this?"
is a String
, not a reader.
However,
tom
has no argument names. How isask
able to run?
There isn't any named argument, but there is still an argument.
Can I think of
tom :: Reader String String
as something like C++ generics whereReader
is bound to some static class in compile time, which always produces"Who is this?"
when you call ask on it?
Definitely not. ask
is not a thing you can pass a Reader
argument to; it is already a Reader
action all by itself. You can't call ask
on tom
. You can think of tom :: Reader String String
as something like tom :: String -> String
. You can also think of ask
as something like ask :: String -> String
that can be used during the implementation of tom
if you want (but no obligation).
Because as far as I know,
ask
is something that takes aReader String String
and it's not explicitly getting it anywhere as an argument.
No. See previous point. ask
is a Reader String String
, it doesn't take one.