可以使用显式字典传递重新绑定(>> =)并返回monad:
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RebindableSyntax #-}
module Lib where
import Prelude hiding ((>>=), return)
data MonadDict m = MonadDict {
bind :: forall a b. m a -> (a -> m b) -> m b ,
ret :: forall a. a -> m a }
(>>=) :: (MonadDict m -> m a) -> (a -> (MonadDict m -> m b)) -> (MonadDict m -> m b)
return :: a -> (MonadDict m -> m a)
monadDictIO :: MonadDict IO
usage = let
monadicCode = do
ln <- const getLine
const . putStrLn $ ln
in monadicCode monadDictIO
有没有更好的方法,如何表示monad,这样可以避免在每次使用monadic动作时忽略MonadDict
monad实例参数(使用const
)?
答案 0 :(得分:6)
你可以这样做:
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RebindableSyntax #-}
{-# LANGUAGE RecordWildCards #-}
module Lib where
import Prelude hiding(return, fail, (>>=), (>>))
data MonadDict m = MonadDict
{ (>>=) :: forall a b. m a -> (a -> m b) -> m b
, (>>) :: forall a b. m a -> m b -> m b
, return :: forall a. a -> m a
, fail :: forall a. String -> m a
}
monadDictIO :: MonadDict IO
monadDictIO = ...
foo :: MonadDict m -> String -> m ()
foo = ...
usage = let
monadicCode m@MonadDict{..} = do
ln <- getLine
putStrLn ln
foo m ln
in monadicCode monadDictIO
答案 1 :(得分:1)
简短且错误的答案是将MonadDict m
参数从第二个参数的返回类型中删除到(>>=)
:
(>>=) :: (MonadDict m -> m a) -> (a -> m b) -> (MonadDict m -> m b)
但这并没有真正解决所有语法问题。如果某人有一个类型为Monad m => a -> m b
的现有箭头,并且显式字典传递它并且类型为a -> (MonadDict m -> m b)
,并且不能用作{{1}的第二个参数}。如果有一个函数(>>=)
使其与第二个参数兼容,那么就没有理由通过drop :: (MonadDict m -> m b) -> m b
。
您重新发明ReaderT
转换器以阅读MonadDict
。
MonadDict m
每次您使用newtype ReaderT r m a = ReaderT { runReaderT :: r -> m a }
时,const
lift
等同于m a
。如果您使用ReaderT (MonadDict m) m a
而不是lift
编写示例,那么您的示例就不会那么陌生。
const
以下是使用usage = let
monadicCode = do
ln <- lift getLine
lift . putStrLn $ ln
in monadicCode monadDictIO
的完整示例;最好为ReaderT
创建一个新类型,为ReaderT (MonadDict m) m
创建一个不同的名称。 lift
和(>>=)
的实施与return
相同,但它使用ReaderT
中的bind
或ret
。
MonadDict
如果您给它自己的类型,您可以为它配备独立于基础{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RebindableSyntax #-}
module Lib
( usage
) where
import Prelude hiding ((>>=), return)
import qualified Prelude as P ((>>=), return)
import Control.Monad.Trans.Reader
data MonadDict m = MonadDict {
bind :: forall a b. m a -> (a -> m b) -> m b ,
ret :: forall a. a -> m a }
type ReadM m a = ReaderT (MonadDict m) m a
(>>=) :: ReadM m a -> (a -> ReadM m b) -> ReadM m b
m >>= k = ReaderT $ \d@MonadDict { bind = bind } -> bind (runReaderT m d) (\a -> runReaderT (k a) d)
return :: a -> ReadM m a
return a = ReaderT $ \d@MonadDict { ret = ret } -> ret a
lift :: m a -> ReadM m a
lift m = ReaderT $ \_ -> m
monadDict :: Monad m => MonadDict m
monadDict = MonadDict {
bind = (P.>>=),
ret = P.return
}
example1 :: String -> ReadM IO ()
example1 a = do
lift . putStrLn $ a
lift . putStrLn $ a
example2 :: ReadM IO ()
example2 = do
example1 "Hello"
ln <- lift getLine
lift . putStrLn $ ln
usage :: IO ()
usage = runReaderT example2 monadDict
的{{1}}实例,并免除Monad
。
m