Rebind用无类型类monad表示

时间:2016-12-07 16:41:09

标签: haskell monads typeclass do-notation

可以使用显式字典传递重新绑定(>> =)并返回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)?

2 个答案:

答案 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中的bindret

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