কেন এই "সংঘাতের কৌশল" ম্যানুয়ালি সংজ্ঞায়িত হ্যাসফিল্ডে কাজ করছে না?


9

আমার কাছে এই (স্বীকারোচিতভাবে অদ্ভুত) কোড রয়েছে যা লেন্স এবং জিএইচসি.রেকর্ডগুলি ব্যবহার করে :

{-# 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, তাই runGetterfield ক্ষেত্রের ধরণের মাধ্যমে HasFieldঅবশ্যই আবশ্যক হওয়া উচিত ReifiedGetter Person vএবং তা rঅবশ্যই Person
  • কারণ rহল Person, এ উত্স প্রকার HasFieldহওয়া আবশ্যক Glass Person। আমরা এখন এর Glassyজন্য তুচ্ছ উদাহরণটি সমাধান করতে পারি the
  • কী kমধ্যে HasFieldএকটি টাইপ আক্ষরিক হিসেবে দেওয়া হয়: Symbol name
  • আমরা উদাহরণ পূর্বশর্ত চেক। আমরা জানি kএবং r, এবং তারা যৌথভাবে কার্যকরী নির্ভরতার vকারণে নির্ধারণ করে HasField। উদাহরণটি বিদ্যমান (রেকর্ড ধরণের জন্য স্বয়ংক্রিয়ভাবে উত্পন্ন) এবং এখন আমরা জানি যে vএটি String। আমরা সব ধরণের সাফল্যের সাথে বিশৃঙ্খলাবদ্ধ হয়েছি।
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.