Tại sao không phải là ràng buộc của trò lừa bịp mà làm việc trong trường hợp HasField được xác định thủ công này?


9

Tôi có mã này (phải thừa nhận là lạ) sử dụng ống kínhGHC.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)

Ý tưởng là có một HasFieldví dụ gợi ReifiedGetterra từ một proxy, chỉ vì địa ngục của nó. Nhưng nó không hoạt động:

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

Tôi không hiểu tại sao r0vẫn còn mơ hồ. Tôi đã sử dụng thủ thuật ràng buộc và trực giác của tôi là phần đầu cá thể phải khớp, sau đó bộ đánh máy sẽ tìm thấy r0 ~ Persontrong các điều kiện tiên quyết và điều đó sẽ loại bỏ sự mơ hồ.

Nếu tôi thay đổi (HasField k r v, x ~ r)thành (HasField k r v, Glass x ~ Glass r)điều đó sẽ loại bỏ sự mơ hồ và nó sẽ biên dịch tốt. Nhưng tại sao nó hoạt động, và tại sao nó không hoạt động theo cách khác?

Câu trả lời:


9

Có lẽ đáng ngạc nhiên, nó phải làm với Glassđa loại:

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

Trong khi đó, không giống như tham số loại Glass, "bản ghi" HasFieldphải là loại Type:

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

Nếu tôi thêm một chữ ký loại độc lập như thế này:

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

sau đó nó đánh máy ngay cả với (HasField k r v, x ~ r).


Trong thực tế, với chữ ký loại, "thủ thuật ràng buộc" không còn cần thiết:

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)

Ở đây, luồng thông tin trong quá trình đánh máy dường như là:

  • Chúng tôi biết chúng tôi có một Person , vì vậy, thông qua runGetterloại hình trường trong trường HasFieldphải ReifiedGetter Person vrphải Person.
  • Bởi vì rPerson, loại nguồn trong HasFieldphải Glass Person. Bây giờ chúng ta có thể giải quyết Glassytrường hợp tầm thường cho the.
  • Chìa khóa ktrong HasFieldđược đưa ra như là một loại đen: các Symbol name.
  • Chúng tôi kiểm tra các điều kiện tiên quyết. Chúng tôi biết kr, và họ cùng xác định vvì sự HasFieldphụ thuộc chức năng. Trường hợp tồn tại (được tạo tự động cho các loại bản ghi) và bây giờ chúng ta biết rằng đó vString . Chúng tôi đã định hướng thành công tất cả các loại.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.