การสร้างการต่อข้อมูลที่สัมพันธ์กันอย่างสมบูรณ์


10

ความจริงที่ดีเกี่ยวกับการต่อเรียงคือถ้าฉันรู้ว่ามีสองตัวแปรในสมการ:

a ++ b = c

จากนั้นฉันก็รู้สาม

ฉันต้องการที่จะจับภาพความคิดนี้ใน concat ของตัวเองดังนั้นฉันจึงใช้การพึ่งพาการทำงาน

{-# 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และมันอาจจะเป็นกรณีถ้า

ฉันจะปรับอินสแตนซ์ของฉันเพื่อให้การพึ่งพาทั้งสามนั้นเป็นที่พอใจได้อย่างไร


1
ปัญหาที่สำคัญดูเหมือนจะเป็นbs cs -> asเพราะเราต้องการข้อมูลที่ไม่ใช่ท้องถิ่นเกี่ยวกับbsและcsตัดสินใจว่าasควรจะเป็นข้อเสียหรือศูนย์ เราจำเป็นต้องค้นหาวิธีการนำเสนอข้อมูลนี้ เราจะเพิ่มบริบทใดลงในลายเซ็นประเภทเพื่อรับประกันเมื่อไม่สามารถอนุมานได้โดยตรง
luqui

3
เพื่อขยายสิ่ง luqui กล่าวว่าจินตนาการที่เรารู้bsและcsและเราต้องการที่จะใช้ประโยชน์จาก fundep asคือเราต้องการที่จะสร้างใหม่ หากต้องการดำเนินการในลักษณะที่กำหนดขึ้นมาเราคาดว่าจะสามารถส่งมอบอินสแตนซ์เดียวและทำตามสูตรนั้นได้ รูปธรรมสมมติและbs = (Int ': bs2) cs = (Int ': cs2)เราเลือกอินสแตนซ์ใด มันเป็นไปได้ที่ดังกล่าวIntในcsมาจากbs(และasเป็นที่ว่างเปล่า) นอกจากนี้ยังเป็นไปได้ที่มาจาก (ไม่ว่างเปล่า) asและIntจะปรากฏขึ้นอีกครั้งในcsภายหลัง เราต้องเจาะลึกลงcsไปเพื่อรู้และ GHC จะไม่ทำเช่นนั้น
Chi

1
GHC จะรับเงินที่สามารถพิสูจน์ได้โดยใช้วิธีการเหนี่ยวนำที่เรียบง่ายจากอินสแตนซ์ที่เรียบง่าย ที่นี่หนึ่งในนั้นต้องมีการเหนี่ยวนำสองครั้งเพื่อพิสูจน์ (หรือดูเหมือน) และคอมไพเลอร์จะไม่ไปไกล
Chi

คำตอบ:


10

ดังนั้นตามความเห็นที่แนะนำ GHC ไม่ได้คิดออกมาเอง แต่เราสามารถช่วยได้ในการเขียนโปรแกรมระดับประเภทเล็กน้อย TypeFamiliesขอแนะนำบาง ฟังก์ชั่นทั้งหมดเหล่านี้เป็นการแปลแบบลิสเทอร์ไปหน้าระดับของการแปลที่ตรงไปตรงมา

-- | 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)

ด้วยเครื่องมือนี้เราสามารถไปถึงเป้าหมายชั่วโมง แต่ก่อนอื่นเรามากำหนดฟังก์ชั่นด้วยคุณสมบัติที่ต้องการ:

  • ความสามารถในการอนุมานcsจากasและbs
  • ความสามารถในการอนุมานasจากbsและcs
  • ความสามารถในการอนุมานbsจากasและcs

voila:

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

มาทดสอบกันเถอะ:

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]

และในที่สุดเป้าหมายสุดท้าย:

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
ทำได้ดี! ฉันยังสงสัยว่าสิ่งนี้อาจเป็นไปไม่ได้ แต่คุณแก้ไขได้อย่างโปร่งใสและสวยงาม
luqui

ขอขอบคุณ @luqui
lehins
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.