Tamamen bağımlı bir birleştirme oluşturma


10

Birleştirme hakkında güzel bir gerçek gerçeği, denklemde herhangi bir iki değişken biliyorsanız:

a ++ b = c

Sonra üçüncüyü biliyorum.

Bu fikri kendi aklımda yakalamak istiyorum, böylece işlevsel bir bağımlılık kullanıyorum.

{-# 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

Şimdi ben heterojen liste şöyle çağrıştırın:

data HList ( as :: [ Type ] ) where
  HEmpty :: HList '[]
  HCons  :: a -> HList as -> HList (a ': as)

Ama bunları Concatablebir sorun olarak ilan etmeye çalıştığımda

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)

Üçüncü fonksiyonel bağımlılığımı tatmin etmiyorum. Daha doğrusu derleyici bizim yapmadığımıza inanıyor. Bunun nedeni, derleyicinin ikinci örneğimizde durumun böyle olabileceğine inanmasıdır bs ~ (a ': cs). Ve eğer durum böyle olabilir Concatable as (a ': cs) cs.

Örneklerimi üç bağımlılığın da karşılanması için nasıl ayarlayabilirim?


1
Temel sorun şudur bs cs -> as, çünkü eksileri veya sıfır olması konusunda yerel olmayan bilgilere ihtiyacımız var bsve cskarar vermemiz gerekiyor as. Bu bilgileri nasıl temsil edeceğimizi bulmamız gerekiyor; doğrudan çıkarılamadığında garanti altına almak için bir tip imzasına hangi bağlam ekleriz?
luqui

3
Luqui'nin söylediklerini genişletmek için: bildiğimizi bsve csfundep'ten yararlanmak istediğimizi, yani yeniden inşa etmek istediğimizi hayal edin as. Bunu deterministik bir şekilde yapmak için, tek bir örnek vermeyi ve bu tarifi takip etmeyi umuyoruz. Somut olarak, varsayalım bs = (Int ': bs2)ve cs = (Int ': cs2). Hangi örneği seçiyoruz? Böyle olması mümkündür Intiçinde csgelir bs(ve asboş). Bunun asyerine (boş) da olabilir ve daha sonra Inttekrar ortaya çıkacaktır cs. csBilmek için daha derine inmemiz gerekiyor ve GHC bunu yapmayacak.
chi

1
Çok kabaca söylemek gerekirse, GHC, örneklerden basit bir indüksiyon formu kullanılarak kanıtlanabilen fundepsleri kabul edecektir. Burada, bunlardan biri kanıtlanmak için bir tür çift indüksiyon gerektirir (veya öyle görünüyor) ve derleyici o kadar ileri gitmeyecek.
chi

Yanıtlar:


10

Yani, yorumların önerdiği gibi, GHC bunu kendi başına çözmeyecek, ancak biraz tür düzeyinde programlama ile yardımcı olabiliriz. Biraz tanıtalım TypeFamilies. Bu işlevlerin tümü, tür düzeyine yükseltilmiş liste manipülasyonunun oldukça basit çevirileridir:

-- | This will produce the suffix of `cs` without `as`
type family DropPrefix (as :: [Type]) (cs :: [Type]) where
  DropPrefix '[] cs = cs
  DropPrefix (a ': as) (a ': cs) = DropPrefix as cs

-- Similar to the logic in the question itself: list concatenation. 
type family Concat (as :: [Type]) (bs :: [Type]) where
  Concat '[] bs = bs
  Concat (head ': tail) bs = head ': Concat tail bs

-- | Naive list reversal with help of concatenation.
type family Reverse (xs :: [Type]) where
  Reverse '[] = '[]
  Reverse (x ': xs) = Concat (Reverse xs) '[x]

-- | This will produce the prefix of `cs` without `bs`
type family DropSuffix (bs :: [Type]) (cs :: [Type]) where
  DropSuffix bs cs = Reverse (DropPrefix (Reverse bs) (Reverse cs))

-- | Same definition of `HList` as in the question
data HList (as :: [Type]) where
  HEmpty :: HList '[]
  HCons :: a -> HList as -> HList (a ': as)

-- | Definition of concatenation at the value level
concatHList :: (cs ~ Concat as bs) => HList as -> HList bs -> HList cs
concatHList HEmpty bs = bs
concatHList (HCons head tail) bs = HCons head (concatHList tail bs)

Elimizdeki bu araçlar ile aslında saatlik hedefe ulaşabiliriz, ancak önce istenen özelliklere sahip bir işlev tanımlayalım:

  • Anlamak yeteneği csgelen asvebs
  • Anlamak yeteneği asgelen bsvecs
  • Anlamak yeteneği bsgelen asvecs

Voila:

concatH ::
     (cs ~ Concat as bs, bs ~ DropPrefix as cs, as ~ DropSuffix bs cs)
  => HList as
  -> HList bs
  -> HList cs
concatH = concatHList

Test edelim:

foo :: HList '[Char, Bool]
foo = HCons 'a' (HCons True HEmpty)

bar :: HList '[Int]
bar = HCons (1 :: Int) HEmpty
λ> :t concatH foo bar
concatH foo bar :: HList '[Char, Bool, Int]
λ> :t concatH bar foo
concatH bar foo :: HList '[Int, Char, Bool]

Ve son olarak hedef:

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

instance (cs ~ Concat as bs, bs ~ DropPrefix as cs, as ~ DropSuffix bs cs) =>
         Concatable HList as bs cs where
  concat' = concatH
λ> :t concat' HEmpty bar
concat' HEmpty bar :: HList '[Int]
λ> :t concat' foo bar
concat' foo bar :: HList '[Char, Bool, Int]
λ> :t concat' bar foo
concat' bar foo :: HList '[Int, Char, Bool]

3
Aferin! Bunun imkansız olabileceğinden bile şüpheliydim ama şeffaf ve zarif bir şekilde çözdün.
luqui

Thank you, @luqui
lehins
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.