我正在阅读Scrap your type classes。这为类型类提供了替代方案。但是,我被Paul Chiusano的评论所困扰,该评论谈到了恢复 do notation 语法。
坦率地说,我无法理解
return :: a -> (Monad f -> f a)
(>>=) :: (Monad f -> f a) -> (a -> (Monad f -> f b)) -> (Monad f -> f b)
将有助于恢复 do notation
你可以实现像这样的所有monadic组合器和desugar 给他们做记号。 do块计算为接受的函数 monad字典,所以你甚至可以方便地编写代码 monad的多态性选择,无需线程化 手动字典。
尤其是,它如何适合上述文章中提到的GADT风格方法的背景?
答案 0 :(得分:6)
{-# LANGUAGE Rank2Types, RebindableSyntax #-} import qualified Prelude import qualified System.IO import Prelude (String, ($), (++)) import System.IO (IO)
Gabriel Gonzales的建议
data MonadI f = MonadI { _return :: forall a . a -> f a, _bind :: forall a b . f a -> (a -> f b) -> f b }
您可以按照以下方式实施必要的功能return
和(>>=)
,其中包含Paul Chiusano建议的类型:
return :: a -> (MonadI f -> f a) return x = \dict -> (_return dict) x (>>=) :: (MonadI f -> f a) -> (a -> (MonadI f -> f b)) -> (MonadI f -> f b) ma >>= f = \dict -> (_bind dict) (ma dict) (\x -> f x dict)
这还不足以恢复通知,因为您还需要(>>)
和(sadly)fail
。您可以按如下方式实现它们:
(>>) :: (MonadI f -> f a) -> (MonadI f -> f b) -> (MonadI f -> f b) ma >> mb = ma >>= (\_ -> mb) fail :: String -> MonadI f -> f a fail str = \_ -> Prelude.error str -- Because let's not further entertain that idea.
现在我们有了编写简单程序所需的工具:
main :: IO () main = (\m -> m monad'IO) $ do putStrLn "What is your name?" name <- getLine putStrLn $ "Hello, " ++ name
当然,我们将不得不从System.IO
借用一些东西:
getLine :: MonadI IO -> IO String getLine = \_ -> System.IO.getLine putStrLn :: String -> (MonadI IO -> IO ()) putStrLn str = \_ -> System.IO.putStrLn str monad'IO :: MonadI IO monad'IO = MonadI { _return = (Prelude.return :: a -> IO a), _bind = ((Prelude.>>=) :: IO a -> (a -> IO b) -> IO b) }