чтения «Пишите себя схемой в течение 48 часов» и путается на этой странице 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)
Я не совсем ясно, как будет решен тип. Вот мои аргументы:
envRef
имеет тип IORef [(String, IORef LispVal)]
, поэтому readIORef envRef
имеет тип IO [(String, IORef LispVal)]
.
Теперь LiftIO
определяется в http://hackage.haskell.org/package/transformers-0.4.1.0/docs/Control-Monad-IO-Class.html как типа liftIO :: IO a -> m a
и реализуется в https://hackage.haskell.org/package/mtl-1.1.0.2/docs/src/Control-Monad-Error.html#ErrorT. Итак, liftIO $ readIORef envRef
возвращает m [(String, IORef LispVal)]
для некоторой монады m (что не имеет значения, потому что мы просто используем <-
на нем сразу, поэтому игнорируем оболочку монады) [1].
Это означает, что env [(String, IORef LispVal)]
, поэтому lookup var env
является Maybe IORefLispVal
. Для ветви Just может быть, liftIO . readIORef
будет m LispVal
, снова для некоторой монады m. Учитывая, что вся функция возвращает IOThrowsError LispVal
(это всего лишь ErrorT LispError IO LispVal
i.e. IO Either LispError LispVal
), поэтому m должно быть IOThrowsError
[2].
Теперь, если существуют разные трансформаторы монады, я предполагаю, что в области может быть более одного лифта с другой подписью типа. Действительно ли это так, и если да, то в какой степени цепочка рассуждений выше представителя того, как Haskell определяет типы? Я недоволен рассуждениями в [1] или [2], поэтому есть вторичный вопрос о том, как определяется монада для лифтинга. И, наконец, для заявления do, как узнать, что такое монада? Это определяется первой монадой после <-
? Или каким-то другим способом?
Спасибо. Таким образом, возврат предложения do должно соответствовать типу возвращаемого выражения, что означает, что оба 'liftIO' должны возвращать значения' IOThrowsError' (поскольку один из них является возвратом предложения do, а другой - <- применяется к нему). Но первый 'liftIO' работает на' IO [(String, IORef LispVal)] ', поэтому я не вижу, каким может быть его тип результата. –
Тип 'liftIO' -' MonadIO m => IO a -> m a'. Поскольку ваш тип 'IOThrowsError a' - это просто' ErrorT LispVal IO a', а 'MonadIO m => ErrorT ema' - это экземпляр' MonadIO', вы можете ограничить тип 'liftIO'' 'liftIO :: IO a -> ErrorT LispVal IO a'. – bheklilr