मैन्युअल रूप से परिभाषित HasField उदाहरण में "बाधा चाल" काम क्यों नहीं कर रहा है?


9

मेरे पास यह (अजीब तरह से) कोड है जो लेंस और GHC.Records का उपयोग करता है :

{-# LANGUAGE DataKinds, PolyKinds, FlexibleInstances, UndecidableInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
module Main where
import Control.Lens
import GHC.Records 

data Glass r = Glass -- just a dumb proxy

class Glassy r where
  the :: Glass r

instance Glassy x where
  the = Glass

instance (HasField k r v, x ~ r)
-- instance (HasField k r v, Glass x ~ Glass r) 
  => HasField k (Glass x) (ReifiedGetter r v) where
  getField _ = Getter (to (getField @k))

data Person = Person { name :: String, age :: Int } 

main :: IO ()
main = do
  putStrLn $ Person "foo" 0 ^. runGetter (getField @"name" the)

विचार एक HasFieldउदाहरण है कि ReifiedGetterएक प्रॉक्सी से बाहर है, बस यह नरक के लिए है। लेकिन यह काम नहीं कर रहा है:

* Ambiguous type variable `r0' arising from a use of `getField'
  prevents the constraint `(HasField
                              "name"
                              (Glass r0)
                              (ReifiedGetter Person [Char]))' from being solved.

मुझे समझ नहीं आता कि r0अस्पष्ट क्यों रहता है। मैंने बाधा चाल का उपयोग किया , और मेरा अंतर्ज्ञान यह है कि उदाहरण के सिर का मिलान होना चाहिए, फिर टाइपराइटर r0 ~ Personपूर्व शर्त में मिल जाएगा , और इससे अस्पष्टता दूर होगी।

अगर मैं इसमें बदल (HasField k r v, x ~ r)जाता हूं (HasField k r v, Glass x ~ Glass r)तो अस्पष्टता दूर हो जाती है और यह ठीक संकलन करता है। लेकिन यह काम क्यों करता है, और यह दूसरे तरीके से काम क्यों नहीं करता है?

जवाबों:


9

शायद आश्चर्यजनक रूप से, इसे Glassबहु-दयालु होने के साथ करना पड़ा :

*Main> :kind! Glass
Glass :: k -> *

इस बीच, प्रकार के पैरामीटर के विपरीत Glass, "रिकॉर्ड" को HasFieldइस प्रकार का होना चाहिए Type:

*Main> :set -XPolyKinds
*Main> import GHC.Records
*Main GHC.Records> :kind HasField
HasField :: k -> * -> * -> Constraint

अगर मैं इस तरह एक स्टैंडअलोन प्रकार का हस्ताक्षर जोड़ता हूं:

{-# LANGUAGE StandaloneKindSignatures #-}
import Data.Kind (Type)
type Glass :: Type -> Type
data Glass r = Glass

इसके बाद यह टाइपकास्ट भी करता है (HasField k r v, x ~ r)


वास्तव में, इस तरह के हस्ताक्षर के साथ, "बाधा चाल" आवश्यक हो जाती है:

instance HasField k r v => HasField k (Glass r) (ReifiedGetter r v) where
  getField _ = Getter (to (getField @k))

main :: IO ()
main = do
  print $ Person "foo" 0 ^. runGetter (getField @"name" the)
  print $ Person "foo" 0 ^. runGetter (getField @"age" the)

यहां, टाइपकास्टिंग के दौरान जानकारी का प्रवाह लगता है:

  • हम जानते हैं कि हमारे पास एक Personऐसा runGetterक्षेत्र है, जो HasFieldहोना चाहिए ReifiedGetter Person vऔर rहोना चाहिएPerson
  • क्योंकि rहै Person, में स्रोत प्रकार HasFieldहोना चाहिए Glass Person। हम अब के लिए तुच्छ Glassyउदाहरण को हल कर सकते हैं the
  • कुंजी kमें HasFieldएक प्रकार शाब्दिक रूप में दिया जाता है: Symbol name
  • हम उदाहरण पूर्व शर्त की जाँच करते हैं। हम जानते हैं kऔर r, और वे संयुक्त रूप vसे HasFieldकार्यात्मक निर्भरता के कारण निर्धारित करते हैं। उदाहरण मौजूद है (रिकॉर्ड प्रकारों के लिए स्वचालित रूप से उत्पन्न) और अब हम जानते हैं कि vहै String। हमने सभी प्रकारों की सफलतापूर्वक अवहेलना की है।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.