我试图写一些看起来类似于“秩2类型”的东西,但是要针对约束。(或者,假设将“秩2类型”定义中的->
改为=>
是有意义的,这可能是不正确的;如果你想出更好的术语,请修改问题)。
安装程序
首先,Suitable
类型类(来自Data.Suitable,rmonad的基类)可以用来表示可以使用的值的类型。
Suitable m a
为了表示值a
可以用作单子m
的一些函数的值(特别地,如果m
是DSL,则其值通常是合适的a
),例如
class PrintSuitable m where
printSuitable :: Suitable m a => a -> m ()
查看RMonad [link]的注解和它的源代码,了解如何使用Suitable。例如,可以定义Suitable m (Map a b)
,并打印Map中的元素数。
个问题
- 目标 *:现在,我有了一个单子转换器
MyMonadT
,只要m
是一个PrintSuitable
示例,我就想让MyMonadT m
成为一个PrintSuitable
示例。 - 秩2约束动机 *:问题在于类型
a
是相对于printSuitable
函数引入的,即没有出现在class
签名中。(对instance
函数实现的附加约束是非法的),在类签名(下面的第2行)中描述所有a
是有意义的。
下面显示了预期的代码。
instance (PrintSuitable m, MonadTrans t,
(forall a. Suitable (t m) a => Suitable m a), -- rank 2 constraint
) => PrintSuitable (t m) where
printSuitable = lift ...
-- MyMonadT doesn't change what values are Suitable, hence the rank 2 expression,
-- (forall a. Suitable (t m) a => Suitable m a) should hold true
data instance Constraints (MyMonadT m) a =
Suitable m a => MyMonadT_Constraints
instance Suitable m a => Suitable (MyMonadT m) a where -- the important line
constraints = MyMonadT_Constraints
instance MonadTrans MyMonadT where ...
-- now, MyMonadT m is a PrintSuitable whenever m is a PrintSuitable
-- the manual solution, without using MonadTrans, looks roughly like this
instance PrintSuitable m => PrintSuitable (t m) where
printSuitable a = withResConstraints $ \MyMonadT_Constraints -> ...
所指出的约束说明任何在(t m)
中合适的东西在m
中也是合适的。如何对功能等价物进行编码?
提前感谢!!!
3条答案
按热度按时间fwzugrvs1#
"做你要求的事"
如果你看一下我的hackage约束包,
Data.Constraint.Forall
这可以用来创建量化约束,使用inst和包中的其他约束组合子,并通过创建一个辅助约束来将参数放在正确的位置,可以直接对所需的内容进行编码。
关于反射机制的描述可以在我的博客上找到。
http://comonad.com/reader/2011/what-constraints-entail-part-1/
http://comonad.com/reader/2011/what-constraints-entail-part-2/
然而,这需要一个出血边缘GHC。
在很多情况下,你可以通过对你的特定约束做一个秩2的版本来模仿它。
但是在您情况下,您不仅需要秩2约束,而且需要约束蕴涵。
用我们能做的机器
然后您可以使用
并使用
expr \\ lowerSuitability
在已知Suitable (t m) a
的上下文中手动将Suitable m a
示例纳入范围。但是,这是一种非常危险的表达示例的方式,因为它阻止了您以任何其他方式 * 将某个事物变成同类(* -〉)-〉 -〉* PrintSuitable * 的示例,并且如果您不小心的话,可能会干扰定义您的基本情况!
"做你需要的"
正确的做法是给予定义一个覆盖所有情况的示例,而是定义一个可用于任何适当的转换器的
printSuitableDefault
。假设丹尼尔的回复中提到的RMonadTrans的存在
我们可以定义:
你不太可能有太多的rmonad转换器,这确保了如果你想以不同的方式打印,你有灵活性。
在最先进的编译器下更好地完成您需要的工作
在7.3.x(当前的GHC HEAD)或更高版本中,您甚至可以使用新的默认声明来使这一点不那么麻烦。
则每个变压器的示例可以如下所示:
且您可以为一些受限单子定义合适的printSuitable基本情况,而不用担心重叠。
alen0pnh2#
我认为
MonadTrans
在这里没有用,因为lift
要求m
是Monad
。sqserrrh3#
相关的一点是,您可以定义一个示例:
所以你不需要约束。这会有帮助吗?