पूरी तरह से निर्भर संघटन बनाना


10

संघनन के बारे में एक अच्छा सच यह है कि अगर मुझे समीकरण में कोई दो चर पता हैं:

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

मैं अपने उदाहरणों को कैसे समायोजित कर सकता हूं ताकि सभी तीन निर्भरताएं संतुष्ट हों?


1
मुख्य परेशानी यह प्रतीत होती है bs cs -> as, क्योंकि हमें गैर-स्थानीय जानकारी की आवश्यकता है bsऔर csयह तय करने के लिए कि क्या asविपक्ष या शून्य होना चाहिए। हमें यह पता लगाने की आवश्यकता है कि इस जानकारी का प्रतिनिधित्व कैसे करें; जब हम सीधे कटौती नहीं कर सकते हैं, तो हम इसकी गारंटी देने के लिए किस प्रकार के हस्ताक्षर को जोड़ेंगे?
लुक्वी

3
लुक्वी ने जो कहा उसका विस्तार करने के लिए: कल्पना कीजिए कि हम जानते हैं bsऔर cs, और हम फंड का फायदा उठाना चाहते हैं, यानी हम पुनर्निर्माण करना चाहते हैं as। नियत तरीके से करने के लिए, हम एक उदाहरण के लिए प्रतिबद्ध होने और उस नुस्खा का पालन करने में सक्षम होने की उम्मीद करते हैं। लगातार, मान bs = (Int ': bs2)और cs = (Int ': cs2)। हम कौन सा उदाहरण चुनते हैं? यह संभव है कि इस तरह Intसे csआता है bs(और asखाली है)। यह भी संभव है कि asइसके बजाय (nonempty) से आता है , और बाद Intमें फिर से दिखाई देगा cs। हमें यह जानने के लिए और खुदाई करने की आवश्यकता है csकि GHC ऐसा नहीं करेगा।
चि

1
बहुत मोटे तौर पर, जीएचसी ऐसे फंडों को स्वीकार करेगा, जिन्हें उदाहरणों से सरल रूप में प्रेरण का उपयोग करके साबित किया जा सकता है। यहां, उनमें से एक को एक तरह के दोहरे प्रेरण की आवश्यकता होती है (या ऐसा लगता है) साबित किया जाता है, और संकलक अब तक नहीं जाएगा।
ची

जवाबों:


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

देखा:

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
lehins
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.