关于串联的一个很好的真实事实是,如果我知道方程式中的任何两个变量:
a ++ b = c
那我知道第三个。
我想在我自己的concat中抓住这个想法,所以我使用了功能依赖。
{-# Language DataKinds, GADTs, FlexibleContexts, FlexibleInstances, FunctionalDependencies, KindSignatures, PolyKinds, TypeOperators, UndecidableInstances #-}
import Data.Kind (Type)
class Concatable
(m :: k -> Type)
(as :: k)
(bs :: k)
(cs :: k)
| as bs -> cs
, as cs -> bs
, bs cs -> as
where
concat' :: m as -> m bs -> m cs
现在,我想到了类似的异类列表:
data HList ( as :: [ Type ] ) where
HEmpty :: HList '[]
HCons :: a -> HList as -> HList (a ': as)
但是当我尝试声明这些时,Concatable
我遇到了问题
instance Concatable HList '[] bs bs where
concat' HEmpty bs = bs
instance
( Concatable HList as bs cs
)
=> Concatable HList (a ': as) bs (a ': cs)
where
concat' (HCons head tail) bs = HCons head (concat' tail bs)
我不满足我的第三个功能依赖性。或者更确切地说,编译器认为我们不这样做。这是因为编译器认为,在第二种情况下,可能是这样bs ~ (a ': cs)
。如果是的话Concatable as (a ': cs) cs
。
如何调整实例,以便满足所有三个依赖关系?
扩展luqui所说的话:假设我们知道
—
志
bs
和cs
,并且我们想利用Fundep,即我们要重构as
。为了以确定性的方式进行操作,我们希望能够提交到单个实例并遵循该配方。具体而言,假设bs = (Int ': bs2)
和cs = (Int ': cs2)
。我们选择哪个实例?此类Int
输入可能cs
来自bs
(并且as
为空)。也有可能来自(非空)as
,而稍后Int
将再次出现cs
。我们需要更深入cs
地了解,GHC不会这样做。
粗略地说,GHC会接受脚底肌,这可以通过实例中的简单诱导来证明。在这里,其中一个需要一种双重归纳的证明(或者看起来),并且编译器不会走那么远。
—
志
bs cs -> as
,因为我们需要有关非本地信息,bs
并cs
确定as
该值是弊还是零。我们需要找出如何表示这些信息;当无法直接推导类型签名时,我们将添加什么上下文来保证它呢?