Хороший факт о конкатенации заключается в том, что если я знаю какие-либо две переменные в уравнении:
a ++ b = c
Тогда я знаю третий.
Я хотел бы запечатлеть эту идею в моем собственном конкатате, поэтому я использую функциональную зависимость.
{-# 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
.
Как я могу настроить свои экземпляры так, чтобы все три зависимости были удовлетворены?
bs
и cs
, и мы хотим использовать фундеп, то есть мы хотим реконструировать as
. Чтобы сделать это детерминированным способом, мы ожидаем, что сможем зафиксировать один экземпляр и следовать этому рецепту. Конкретно предположим bs = (Int ': bs2)
и cs = (Int ': cs2)
. Какой экземпляр мы выбираем? Вполне возможно , что такое Int
в cs
исходит от bs
(и as
пуста). Также возможно, что as
вместо этого есть (непустое) , и это Int
появится снова cs
позже. Нам нужно копнуть глубже, cs
чтобы знать, и GHC не будет этого делать.
bs cs -> as
, что нам нужна нелокальная информация оbs
иcs
решить,as
должны ли быть минусы или ноль. Нам нужно выяснить, как представлять эту информацию; какой контекст мы добавим к сигнатуре типа, чтобы гарантировать ее, когда она не может быть выведена напрямую?