[英]Constraining a derived variable of a type class or an instance
I'm writing a type class for my pipes
library to define an abstract interface to Proxy
-like types. 我正在为我的
pipes
库编写一个类型类来定义类似于Proxy
的类型的抽象接口。 The type class looks something like: 类型类看起来像:
class ProxyC p where
idT :: (Monad m) => b' -> p a' a b' b m r
(<-<) :: (Monad m)
=> (c' -> p b' b c' c m r)
-> (b' -> p a' a b' b m r)
-> (c' -> p a' a c' c m r)
... -- other methods
I'm also writing extensions for the Proxy
type that are of the form: 我也在编写以下形式的
Proxy
类型的扩展:
instance (ProxyC p) => ProxyC (SomeExtension p) where ....
... and I'd like these instances to be able to impose an additional constraint that if m
is a Monad
then pa' ab' bm
is a Monad
for all a'
, a
, b'
, and b
. ...并且我希望这些实例能够施加一个额外的约束,即如果
m
是Monad
那么pa' ab' bm
是所有a'
, a
, b'
和b
的Monad
。
However, I have no clue how to cleanly encode that as a constraint either for the ProxyC
class or for the instances. 但是,我不知道如何干净地将其编码为
ProxyC
类或实例的约束。 The only solution I currently know of is to do something like encoding it in the method signatures of the class: 我目前所知道的唯一解决方案是在类的方法签名中对其进行编码:
(<-<) :: (Monad m, Monad (p b' b c' c m), Monad (p a' a b' b m))
=> (c' -> p b' b c' c m r)
-> (b' -> p a' a b' b m r)
-> (c' -> p a' a c' c m r)
... but I was hoping there would be a simpler and more elegant solution. ...但我希望有一个更简单,更优雅的解决方案。
Edit : And not even that last solution works, since the compiler does not deduce that
(Monad (SomeExtension pa' ab' bm))
implies (Monad (pa' ab' bm))
for a specific choice of variables, even when given the following instance: 编辑 :甚至最后一个解决方案都不起作用,因为编译器没有推断出
(Monad (SomeExtension pa' ab' bm))
暗示(Monad (pa' ab' bm))
(Monad (SomeExtension pa' ab' bm))
(Monad (pa' ab' bm))
特定的变量选择,即使给定了以下实例:
instance (Monad (p a b m)) => Monad (SomeExtension p a b m) where ...
Edit #2 : The next solution I'm considering is just duplicating the methods for the
Monad
class within the ProxyC
class: 编辑#2 :我正在考虑的下一个解决方案是复制
ProxyC
类中Monad
类的方法:
class ProxyC p where
return' :: (Monad m) => r -> p a' a b' b m r
(!>=) :: (Monad m) => ...
... and then instantiating them with each ProxyC
instance. ...然后用每个
ProxyC
实例实例化它们。 This seems okay for my purposes since the Monad
methods only need to be used internally for extension writing and the original type still has a proper Monad
instance for the downstream user. 这似乎可以用于我的目的,因为
Monad
方法只需要在内部用于扩展写入,并且原始类型仍然具有适合下游用户的Monad
实例。 All this does is just expose the Monad
methods to the instance writer. 所有这一切只是将
Monad
方法暴露给实例编写器。
a rather trivial way to do this is use a GADT to move the proof to the value level 一个相当简单的方法是使用GADT将证明移动到价值水平
data IsMonad m where
IsMonad :: Monad m => IsMonad m
class ProxyC p where
getProxyMonad :: Monad m => IsMonad (p a' a b' b m)
you will need to explicitly open the dictionary wherever you need it 您需要在任何需要的地方明确打开字典
--help avoid type signatures
monadOf :: IsMonad m -> m a -> IsMonad m
monadOf = const
--later on
case getProxyMonad `monadOf` ... of
IsMonad -> ...
the tactic of using GADTs to pass proofs of propositions is really very general. 使用GADT传递命题证明的策略非常普遍。 If you are okay using constraint kinds, and not just GADTs, you can instead use Edward Kmett's
Data.Constraint
package 如果你可以使用约束种类,而不仅仅是GADT,你可以使用Edward Kmett的
Data.Constraint
包
class ProxyC p where
getProxyMonad :: Monad m => Dict (Monad (p a' a b' b m))
which lets you defined 这让你定义
getProxyMonad' :: ProxyC p => (Monad m) :- (Monad (p a' a b' b m))
getProxyMonad' = Sub getProxyMonad
and then use a fancy infix operator to tell the compiler where to look for the monad instance 然后使用花哨的中缀运算符告诉编译器在哪里查找monad实例
... \\ getProxyMonad'
in fact, the :-
entailment type forms a category (where the objects are constraints), and this category has lots of nice structure, which is to say it is pretty nice to do proofs with. 事实上,
:-
entailment类型形成一个类别(对象是约束),这个类别有很多很好的结构,也就是说用它做证明是相当不错的。
ps none of these snippets are tested. ps这些片段都没有经过测试。
edit: you could also combine the value level proofs with a newtype wrapper and not need to open GADTs all over the place 编辑:您还可以将值级别证明与newtype包装器结合使用,而不需要在整个地方打开GADT
newtype WrapP p a' a b' b m r = WrapP {unWrapP :: p a' a b' b m r}
instance ProxyC p => Monad (WrapP p) where
return = case getProxyMonad of
Dict -> WrapP . return
(>>=) = case getProxyMonad of
Dict -> \m f -> WrapP $ (unWrapP m) >>= (unWrapP . f)
instance ProxyC p => ProxyC (WrapP p) where
...
I suspect, but obviously have not tested, that this implementation will also be relatively efficient. 我怀疑,但显然没有测试过,这个实现也会相对有效。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.