Приємний справжній факт конкатенації полягає в тому, що якщо я знаю будь-які дві змінні рівняння:
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має бути мінус чи нуль. Нам потрібно з'ясувати, як представити цю інформацію; який контекст ми б додали до підпису типу, щоб гарантувати його, коли його неможливо безпосередньо вивести?