Mengapa "trik kendala" tidak berfungsi dalam instance HasField yang ditentukan secara manual ini?


9

Saya memiliki kode ini (memang aneh) yang menggunakan lensa dan GHC.Rekam :

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

Idenya adalah memiliki HasFieldcontoh yang memunculkan ReifiedGetterkeluar dari proxy, hanya untuk itu. Tapi itu tidak berfungsi:

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

Saya tidak mengerti mengapa r0tetap ambigu. Saya menggunakan trik kendala , dan intuisi saya adalah bahwa head instance harus cocok, maka typechecker akan menemukan r0 ~ Persondalam prasyarat, dan itu akan menghapus ambiguitas.

Jika saya berubah (HasField k r v, x ~ r)menjadi (HasField k r v, Glass x ~ Glass r)yang menghapus ambiguitas dan mengkompilasi dengan baik. Tetapi mengapa itu berhasil, dan mengapa itu tidak bekerja sebaliknya?

Jawaban:


9

Mungkin mengejutkan, itu ada hubungannya dengan Glassmenjadi poli-kind:

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

Sementara itu, tidak seperti parameter tipe Glass, "record" di HasFieldharus dari jenis Type:

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

Jika saya menambahkan tanda tangan jenis mandiri seperti ini:

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

maka itu ketik pemeriksaan bahkan dengan (HasField k r v, x ~ r).


Bahkan, dengan tanda tangan yang baik, "trik kendala" tidak lagi diperlukan:

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)

Di sini, aliran informasi selama pemeriksaan ketik tampaknya:

  • Kami tahu kami memiliki Person, jadi — melalui runGetter— jenis bidang dalam HasFieldmust be ReifiedGetter Person vdan rmust be Person.
  • Karena ritu Person, jenis sumber di HasFieldharus Glass Person. Kita sekarang dapat menyelesaikan Glassycontoh sepele untuk the.
  • Kunci kdalam HasFielddiberikan sebagai tipe literal: the Symbol name.
  • Kami memeriksa prasyarat contoh. Kami tahu kdan r, dan mereka bersama-sama menentukanv karena HasFieldketergantungan fungsional. Contoh ada (otomatis dihasilkan untuk jenis catatan) dan sekarang kita tahu bahwa vadalah String. Kami telah berhasil membuat semua jenis ambigu.
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.