As the type allowed in this statement
Reading "Write yourself a schema in 48 hours" and I am confused on this page https://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_Hours/Adding_Variables_and_Assignment :
getVar :: Env -> String -> IOThrowsError LispVal
getVar envRef var = do env <- liftIO $ readIORef envRef
maybe (throwError $ UnboundVar "Getting an unbound variable" var)
(liftIO . readIORef)
(lookup var env)
I don't quite understand how this type is resolved. Here's my reasoning:
envRef
has a type IORef [(String, IORef LispVal)]
, therefore readIORef envRef
has a type IO [(String, IORef LispVal)]
.
Now LiftIO
defined at http://hackage.haskell.org/package/transformers-0.4.1.0/docs/Control-Monad-IO-Class.html as a type type liftIO :: IO a -> m a
and implemented at https://hackage.haskell.org/package/ mtl-1.1.0.2 / docs / src / Control-Monad-Error.html # ErrorT . So it liftIO $ readIORef envRef
returns m [(String, IORef LispVal)]
m for some monad (which doesn't matter, because we just use <-
on it right away, so we ignore the monad wrapper) [1].
This means env [(String, IORef LispVal)]
, therefore lookup var env
- Maybe IORefLispVal
. For the Just branch, it may liftIO . readIORef
be m LispVal
, again for some monad m. Considering that the whole function returns IOThrowsError LispVal
(it's just ErrorT LispError IO LispVal
ie IO Either LispError LispVal
), so m should be IOThrowsError
[2].
Now, if different monad transformers exist, my guess is that there could be more than one elevator in the area with a different type signature. Is this true, and if so, to what extent is the line of reasoning above representative of how Haskell defines types? I am not happy with the reasoning in [1] or [2], so there is a secondary question about how the lifting monad is defined. And finally, for the do statement, how do you know what a monad is? Is it determined by the first monad after <-
? Or in some other way?
source to share
If you have liftIO $ readIORef envRef
, with type MonadIO m => m [(String, IORef LispVal)]
, the question m
matters! It can only be a monad that implements a class MonadIO
and cannot be simply ignored. It must be the same monad that the operator returns maybe ...
. In general, when working with musical notation, all statements within a given block must have the same monad, so you could do
main :: IO ()
main = do
putStrLn "Testing"
x <- getLine
putStrLn x
But you cannot do
main :: IO ()
main = do
putStrLn "Testing"
x <- Just "Doesn't work!"
putStrLn x
Because IO
and Maybe
not in the same Monad! It also main
states that it is of type IO ()
, so you may not be able to return anything other than an action IO
.
If you don't know which type can be genericized too, I would recommend loading it into GHCi and checking it out without the specified type signature. I believe it will turn out to be something like
getVar :: (Eq a, MonadIO m, MonadError LispVal m) => IORef [(a, IORef LispVal)] -> a -> m b
because liftIO $ readIORef envRef
u liftIO . readIORef
means the monad must be an instance MonadIO
, to execute lookup
you need your keys to be an instance Eq
, and throwError $ UnboundVar "..."
> means it must be an instance MonadError LispVal
. There is nothing that makes the rest of the signature return LispVal
s or be the specific monad you are using IOThrowsError
.
source to share