Dobrym prawdziwym faktem na temat konkatenacji jest to, że jeśli znam dowolne dwie zmienne w równaniu:
a ++ b = c
Więc znam trzeci.
Chciałbym uchwycić ten pomysł w swoim własnym konkat, więc używam zależności funkcjonalnej.
{-# 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
Teraz wyczaruję listę heterogeniczną w ten sposób:
data HList ( as :: [ Type ] ) where
HEmpty :: HList '[]
HCons :: a -> HList as -> HList (a ': as)
Ale kiedy próbuję zadeklarować je jako Concatable
problem
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)
Nie spełniam mojej trzeciej zależności funkcjonalnej. A raczej kompilator uważa, że nie. Jest tak, ponieważ kompilator uważa, że w naszym drugim przypadku może tak być bs ~ (a ': cs)
. I może tak być, jeśli Concatable as (a ': cs) cs
.
Jak mogę dostosować moje wystąpienia, aby wszystkie trzy zależności były spełnione?
haskell
typeclass
functional-dependencies
type-level-computation
Sriotchilism O'Zaic
źródło
źródło
bs cs -> as
, że kluczowym problemem jest to , że potrzebujemy nielokalnych informacji na tematbs
ics
decydować, czyas
powinny to być minusy czy zero. Musimy dowiedzieć się, jak reprezentować te informacje; jaki kontekst dodalibyśmy do podpisu typu, aby zagwarantować go, gdy nie można go bezpośrednio wywnioskować?bs
ics
chcemy wykorzystać fundep, czyli chcemy zrekonstruowaćas
. Aby to zrobić w sposób deterministyczny, oczekujemy, że będziemy w stanie zatwierdzić jedną instancję i zastosować się do tego przepisu. Konkretnie, załóżbs = (Int ': bs2)
ics = (Int ': cs2)
. Którą instancję wybieramy? Jest możliwe, że takiInt
wcs
pochodzi zbs
(ias
jest pusta). Możliwe jest również, że pochodzi z (niepuste)as
, iInt
pojawi się ponowniecs
później. Musimy wniknąć głębiej,cs
aby wiedzieć, a GHC tego nie zrobi.Odpowiedzi:
Tak więc, jak sugerują komentarze, GHC nie rozwiąże tego samodzielnie, ale możemy pomóc przy odrobinie programowania na poziomie czcionki. Przedstawmy trochę
TypeFamilies
. Wszystkie te funkcje są dość prostymi tłumaczeniami manipulacji listami podniesionymi do poziomu typu:Dzięki tym narzędziom, które mamy do dyspozycji, możemy faktycznie osiągnąć cel godzinowy, ale najpierw zdefiniujmy funkcję o pożądanych właściwościach:
cs
podstawieas
ibs
as
podstawiebs
ics
bs
podstawieas
ics
Voila:
Przetestujmy to:
I wreszcie cel końcowy:
źródło