jak*_*iel 4 monads haskell monad-transformers lifting
所以我在第一个严肃的haskell项目中都有这样的代码:
f :: (MonadTrans t) => ExceptT () (t (StateT A B)) C
f = do mapExceptT lift $ do
lift $ do
...
lift $ do
...
r <- ...
...
return r
>>= \r -> ...
Run Code Online (Sandbox Code Playgroud)
关于我如何实现目标肯定会出现问题(可能有更简单的方法)但目前我有兴趣学习如何以更好的方式处理一堆monad变换器(如果有的话).这是我弄清楚如何进入r上下文B并将其提升到堆栈中更高的monad 的唯一方法.提升整个块而不是初始语句是我自己可以得到的.
我经常最终得到的是lift我发现的链条,liftIO如果深的monad是可以避免的IO.我不知道其他monad的通用方法.
当他最终处理这样的堆栈时,是否存在可以遵循的模式,并且必须在某个级别提取一个值,在不同级别提取不同的值,将这些组合并影响这两个级别中的任何一个或者可能是另一个级别?
可以以某种方式操纵堆栈,既不会提升整个块(导致let和绑定变量限定范围并限制在内部块),也不需要lift . lift . ... lift单独操作?
这通常是monad变换器的一个众所周知的问题.研究人员设计了各种处理方式,其中没有一种显然是"最好的".一些已知的解决方案包括:
mtl方法,它自动升降机在其内置的单子变压器单子的类型类(和仅其内置的单子变压器).这允许您只写,f :: (MonadState A m, MonadError () m) => m C如果这些是您的函数正在使用的monad的唯一功能.由于其极端的不可移植性和一些其他原因,mtl通常被认为是伪弃用的.有关血腥的详细信息,请参阅此页面和此问题.newtype并编写它手动支持的各种monad类型类的实例中.对于Functor,Applicative和Monad,以及堆栈中顶级转换器实现的任何其他类型类,您可以使用GeneralizedNewtypeDeriving让编译器自动为您编写实例; 对于其他类型类,您必须lift为每个方法插入适当数量的调用.这种方法的优点在于它更通用,更易于理解,同时在呼叫站点为您提供相同的灵活性mtl.这种方法的一个大问题是它鼓励对所有操作使用单个"mega-monad"而不是仅指定所需的操作,因为将任何新的monad转换器添加到堆栈需要编写一个全新的实例列表.A"和"一些任意异常抛出能力" 的monad .相反,monad堆栈提供的不同功能在您的程序的心理模型中具有一些语义含义.前一种方法的一个变化是对超出基本的影响创建自定义类型类Functor,Applicative以及Monad和您写的自定义类型的类的实例newtype"d单子来代替.与此处列出的其他方法相比,这具有一个主要优势:您可以在不同位置拥有一个堆栈,其中包含相同monad变换器的多个副本.到目前为止,这是我在自己的程序中使用最多的策略.