Haskell 混合交换单子
我一直在阅读无标签的最终编码,这给了我一个关于mtl的新视角。也就是说,具体的monad堆栈为dsl指定了一个解释器。这使我更清楚地知道为什么需要n^2个实例 当然,也有一些交换单子,它们不需要这样做。最值得注意的用例是分解IO,以便更容易进行推理和测试 我的第一个想法是通过一个实现对MonadIO进行参数化:Haskell 混合交换单子,haskell,Haskell,我一直在阅读无标签的最终编码,这给了我一个关于mtl的新视角。也就是说,具体的monad堆栈为dsl指定了一个解释器。这使我更清楚地知道为什么需要n^2个实例 当然,也有一些交换单子,它们不需要这样做。最值得注意的用例是分解IO,以便更容易进行推理和测试 我的第一个想法是通过一个实现对MonadIO进行参数化: class (Monad m, Monad (Eff m)) => MonadEff m where type Eff m :: (* -> *) liftE
class (Monad m, Monad (Eff m)) => MonadEff m where
type Eff m :: (* -> *)
liftEff :: Eff m a -> m a
令人惊讶的是,这似乎很好。它允许定义一次提升实例
instance MonadEff IO where
type Eff IO = IO
liftEff = id
instance MonadEff Tracer where
type Eff Tracer = Tracer
liftEff = id
instance MonadEff m => MonadEff (ExceptT e m) where
type Eff (ExceptT a m) = Eff m
liftEff = lift . liftEff
-- ...
并轻松合成效果:
g :: (MonadEff m, MonadError String m, ConsoleEff (Eff m)) => m ()
g = liftEff (put "foo") >> throwError "bar"
h :: (MonadEff m, ConsoleEff (Eff m), FileEff (Eff m)) => m ()
h = liftEff $ do
l1 <- get
l2 <- get
c <- read l2
put $ c ++ l1
class FileEff repr where
read :: FilePath -> repr String
write :: FilePath -> String -> repr ()
class ConsoleEff repr where
put :: String -> repr ()
get :: repr String
instance ConsoleEff IO where
get = getLine
put s = putStrLn s
instance ConsoleEff Tracer where
put s = W.tell [s] >> return ()
get = do
i <- S.get
S.put (i+1)
W.tell $ ["putL '" ++ show i ++ "'"]
return $ "$" ++ show i ++ "(get)"
因此,乍一看,这似乎是完美的,而只需要无害的语言扩展和适合现有的mtl基础设施。不过,我以前从未见过这种情况,所以我假设这一切都有一个陷阱
问题是什么?我看不出这对多层有什么帮助。例如,您如何指定参数
m
能够引发异常,而无需拼写ExceptT
层?您的FileEff
和ConsoleEff
实例是什么样的?我怀疑您必须为每个repr
重写它们。MonadEff类型只会替换底层。您还必须为每个repr重写FileEff和ConsoleEff。有用的是,您可以在重用MonadEff时添加嵌入monad transformer的新效果。因此(MonadError字符串m、MonadEff m、FileEff(Eff m))=>m()
可以是任意字符串(IO())
或任意字符串(Tracer())
等等。它还允许在不重新实现所有IO的情况下对ConsoleEff进行单独的实现。我在我的问题中添加了ConsoleEff的示例实现。您可以尝试将其实现为一个库,支持常见的阅读器
,状态
,等等。可以使用mtl
进行基准测试,看看您的表现如何。如果一切都很好,那么在Reddit上发布并获得一些合理的评论:)
exec :: IO a -> IO a
exec = id
trace :: Tracer a -> [String]
trace (Tracer f) = snd . runWriter $ evalStateT f (0::Int)