Хороший факт о конкатенации заключается в том, что если я знаю какие-либо две переменные в уравнении:
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
.
Как я могу настроить свои экземпляры так, чтобы все три зависимости были удовлетворены?
haskell
typeclass
functional-dependencies
type-level-computation
Сриотчизм О'Зайк
источник
источник
bs cs -> as
, что нам нужна нелокальная информация оbs
иcs
решить,as
должны ли быть минусы или ноль. Нам нужно выяснить, как представлять эту информацию; какой контекст мы добавим к сигнатуре типа, чтобы гарантировать ее, когда она не может быть выведена напрямую?bs
иcs
, и мы хотим использовать фундеп, то есть мы хотим реконструироватьas
. Чтобы сделать это детерминированным способом, мы ожидаем, что сможем зафиксировать один экземпляр и следовать этому рецепту. Конкретно предположимbs = (Int ': bs2)
иcs = (Int ': cs2)
. Какой экземпляр мы выбираем? Вполне возможно , что такоеInt
вcs
исходит отbs
(иas
пуста). Также возможно, чтоas
вместо этого есть (непустое) , и этоInt
появится сноваcs
позже. Нам нужно копнуть глубже,cs
чтобы знать, и GHC не будет этого делать.Ответы:
Так что, как показывают комментарии, GHC не собирается разбираться в этом самостоятельно, но мы можем помочь с небольшим программированием на уровне типов. Давайте представим некоторые
TypeFamilies
. Все эти функции - довольно простые переводы манипулирования списком, поднятые до уровня типа:Имея в своем распоряжении эти инструменты, мы на самом деле можем достичь цели на час, но сначала давайте определим функцию с желаемыми свойствами:
cs
изas
иbs
as
изbs
иcs
bs
изas
иcs
Вуаля:
Давайте проверим это:
И, наконец, конечная цель:
источник