เหตุใด“ เคล็ดลับข้อ จำกัด ” จึงไม่ทำงานในอินสแตนซ์ HasField ที่กำหนดด้วยตนเองนี้


9

ฉันมีรหัส (แปลก ๆ ที่ยอมรับ) ซึ่งใช้เลนส์และGHC บันทึก :

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

แล้วมัน typechecks (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ชนิดของฟิลด์ -The ในHasFieldต้องReifiedGetter Person vและจะต้องเป็นrPerson
  • เพราะrเป็นPersonประเภทแหล่งที่มาในต้องHasField Glass Personตอนนี้เราสามารถแก้ไขเล็ก ๆ น้อย ๆเช่นสำหรับGlassythe
  • กุญแจสำคัญkในจะได้รับเป็นชนิดที่แท้จริงคือHasFieldSymbol name
  • เราตรวจสอบเงื่อนไขเบื้องต้นของอินสแตนซ์ เรารู้kและrและพวกเขาร่วมกันพิจารณาvเนื่องจากการHasFieldพึ่งพาการทำงาน อินสแตนซ์ที่มีอยู่ (ที่สร้างขึ้นโดยอัตโนมัติชนิดบันทึก) และตอนนี้เรารู้ว่าเป็นv Stringเราประสบความสำเร็จในการแก้ปัญหาทุกประเภท
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.