使用无类型类的 monad 重新绑定 do 符号
Rebind do notation with typeclass-free monad
可以使用显式字典传递为 monad 重新绑定 (>>=) 和 return:
{-# 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
)?
你可以这样做:
{-# 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
简短且不正确的答案是将 MonadDict m
参数从第二个参数的 return 类型删除到 (>>=)
:
(>>=) :: (MonadDict m -> m a) -> (a -> m b) -> (MonadDict m -> m b)
但这并不能真正解决您所有的语法问题。如果有人有一个 Monad m => a -> m b
类型的现有箭头,通过显式字典传递它将具有 a -> (MonadDict m -> m b)
类型,并且不能用作 (>>=)
的第二个参数。如果有一个函数 drop :: (MonadDict m -> m b) -> m b
使其与第二个参数兼容,那么就没有理由传递 MonadDict
s 了。
您正在重新发明 ReaderT
转换器来读取 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
s 相同,除了它使用 MonadDict
.[=43 中的 bind
或 ret
=]
{-# 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
如果你给它自己的类型,你可以为它配备一个独立于底层 m
的 Monad
实例,并免除 RebindableSyntax
.
newtype ReadMD m a = ReadMD {runReadMD :: MonadDict m -> m a}
instance Functor (ReadMD f) where
fmap = liftM
instance Applicative (ReadMD f) where
pure = return
(<*>) = ap
instance Monad (ReadMD m) where
m >>= k = ReadMD $ \d@MonadDict { bind = bind } -> bind (runReadMD m d) (\a -> runReadMD (k a) d)
return a = ReadMD $ \d@MonadDict { ret = ret } -> ret a
可以使用显式字典传递为 monad 重新绑定 (>>=) 和 return:
{-# 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
)?
你可以这样做:
{-# 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
简短且不正确的答案是将 MonadDict m
参数从第二个参数的 return 类型删除到 (>>=)
:
(>>=) :: (MonadDict m -> m a) -> (a -> m b) -> (MonadDict m -> m b)
但这并不能真正解决您所有的语法问题。如果有人有一个 Monad m => a -> m b
类型的现有箭头,通过显式字典传递它将具有 a -> (MonadDict m -> m b)
类型,并且不能用作 (>>=)
的第二个参数。如果有一个函数 drop :: (MonadDict m -> m b) -> m b
使其与第二个参数兼容,那么就没有理由传递 MonadDict
s 了。
您正在重新发明 ReaderT
转换器来读取 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
s 相同,除了它使用 MonadDict
.[=43 中的 bind
或 ret
=]
{-# 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
如果你给它自己的类型,你可以为它配备一个独立于底层 m
的 Monad
实例,并免除 RebindableSyntax
.
newtype ReadMD m a = ReadMD {runReadMD :: MonadDict m -> m a}
instance Functor (ReadMD f) where
fmap = liftM
instance Applicative (ReadMD f) where
pure = return
(<*>) = ap
instance Monad (ReadMD m) where
m >>= k = ReadMD $ \d@MonadDict { bind = bind } -> bind (runReadMD m d) (\a -> runReadMD (k a) d)
return a = ReadMD $ \d@MonadDict { ret = ret } -> ret a