最近我一直在研究这种类型,我理解它是自由分配函子的编码(有关切线背景,请参阅这个答案 https://stackoverflow.com/a/56822105/2751851):
data Ev g a where
Ev :: ((g x -> x) -> a) -> Ev g a
deriving instance Functor (Ev g)
存在构造函数确保我只能使用Ev g
通过提供多态提取器forall x. g x -> x
,并且自由构造的升降功能可以被赋予兼容类型:
runEv :: Ev g a -> (forall x. g x -> x) -> a
runEv (Ev s) f = s f
evert :: g a -> Ev g a
evert u = Ev $ \f -> f u
revert :: Distributive g => Ev g a -> g a
revert (Ev s) = s <$> distribute id
然而,在尝试给予时存在困难Ev g
a Distributive
实例。鉴于Ev g
最终只是一个具有奇怪参数类型的函数,人们可能希望只是线程distribute
对于函数(相当于(??) :: Functor f => f (a -> b) -> a -> f b
from lens http://hackage.haskell.org/package/lens-4.17.1/docs/Control-Lens-Lens.html#v:-63--63-,并且不以任何方式检查参数类型)通过Ev
包装:
instance Distributive (Ev g) where
distribute = Ev . distribute . fmap (\(Ev s) -> s)
然而,这不起作用:
Flap3.hs:95:53: error:
• Couldn't match type ‘x’ with ‘x0’
‘x’ is a rigid type variable bound by
a pattern with constructor:
Ev :: forall (g :: * -> *) x a. ((g x -> x) -> a) -> Ev g a,
in a lambda abstraction
at Flap3.hs:95:44-47
Expected type: (g x0 -> x0) -> a
Actual type: (g x -> x) -> a
• In the expression: s
In the first argument of ‘fmap’, namely ‘(\ (Ev s) -> s)’
In the second argument of ‘(.)’, namely ‘fmap (\ (Ev s) -> s)’
• Relevant bindings include
s :: (g x -> x) -> a (bound at Flap3.hs:95:47)
|
95 | distribute = Ev . distribute . fmap (\(Ev s) -> s)
| ^
Failed, no modules loaded.
GHC 反对重新包装存在主义,尽管我们在展开和重新包装之间没有做任何不当的事情。我发现的唯一出路就是诉诸unsafeCoerce
:
instance Distributive (Ev g) where
distribute = Ev . distribute . fmap (\(Ev s) -> unsafeCoerce s)
或者,以更谨慎的方式拼写它:
instance Distributive (Ev g) where
distribute = eevee . distribute . fmap getEv
where
getEv :: Ev g a -> (g Any -> Any) -> a
getEv (Ev s) = unsafeCoerce s
eevee :: ((g Any -> Any) -> f a) -> Ev g (f a)
eevee s = Ev (unsafeCoerce s)
是否可以在不使用的情况下解决这个问题unsafeCoerce
?还是真的没有其他办法了?
附加说明:
我相信Ev
是我可以给出的最正确的构造类型,尽管我很高兴被证明是错误的。我所有将量词转移到其他地方的尝试都会导致需要unsafeCoerce
其他地方或到evert
and revert
具有不允许组合它们的类型。
乍一看,这种情况与中描述的情况类似桑迪·马奎尔的这篇博文 https://reasonablypolymorphic.com/blog/existentials/,最终坚持unsafeCoerce
以及。
以下是给予的看法Ev g
a Representable
实例可能会使问题更加明显。正如 dfeuer 所说 https://stackoverflow.com/a/56828434/2751851,这实际上是不可能的;不出所料,我不得不使用unsafeCoerce
again:
-- Cf. dfeuer's answer.
newtype Goop g = Goop { unGoop :: forall y. g y -> y }
instance Representable (Ev g) where
type Rep (Ev g) = Goop g
tabulate f = Ev $ \e -> f (Goop (goopify e))
where
goopify :: (g Any -> Any) -> g x -> x
goopify = unsafeCoerce
index (Ev s) = \(Goop e) -> s e
While goopify
当然看起来很令人担忧,我认为这里有可能是安全的。存在主义编码意味着任何e
传递给包装函数的必然是元素类型上的多态提取器,它专门用于Any
只是因为我要求这样的事情发生。既然如此,forall x. g x -> x
是一个明智的类型e
。这种舞蹈专门针对Any
只是为了立即撤消它unsafeCoerce
之所以需要,是因为 GHC 迫使我通过做出选择来摆脱存在主义。如果我省略了,就会发生这种情况unsafeCoerce
在这种情况下:
Flap4.hs:64:37: error:
• Couldn't match type ‘y’ with ‘x0’
‘y’ is a rigid type variable bound by
a type expected by the context:
forall y. g y -> y
at Flap4.hs:64:32-37
Expected type: g y -> y
Actual type: g x0 -> x0
• In the first argument of ‘Goop’, namely ‘e’
In the first argument of ‘f’, namely ‘(Goop e)’
In the expression: f (Goop e)
• Relevant bindings include
e :: g x0 -> x0 (bound at Flap4.hs:64:24)
|
64 | tabulate f = Ev $ \e -> f (Goop e)
| ^
Failed, no modules loaded.
Prolegomena 需要在这里运行代码:
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
import Data.Distributive
import Data.Functor.Rep
import Unsafe.Coerce
import GHC.Exts (Any)
-- A tangible distributive, for the sake of testing.
data Duo a = Duo { fstDuo :: a, sndDuo :: a }
deriving (Show, Eq, Ord, Functor)
instance Distributive Duo where
distribute u = Duo (fstDuo <$> u) (sndDuo <$> u)