การสร้างเลนส์โพลีมอร์ฟิก

ฉันสามารถสร้างเลนส์สำหรับฟิลด์สุดท้าย (c) ในประเภทข้อมูลของฉันได้โดยทำดังนี้:

{-# LANGUAGE DuplicateRecordFields #-}

data X1 a c = X1 { a' :: a, b' :: Int, c' :: c } 

data X2 a b c = X2 { a' :: a, b' :: b, c' :: c }

class HavingFieldC x cs ct where
  c :: Functor f => (cs -> f ct) -> x cs -> f (x ct)

instance HavingFieldC (X1 a) cs ct  where
  c = lens
    (\X1 { c' } -> c')
    (\X1 {..} v -> X1 {c' = v, ..})

instance HavingFieldC (X2 a b) cs ct where
  c = lens
    (\X2 { c' } -> c')
    (\X2 {..} v -> X2 {c' = v, ..})

มีอะไรที่คล้ายกันที่ฉันสามารถทำได้กับฟิลด์ a และ b หรือไม่


person Vanson Samuel    schedule 13.11.2017    source แหล่งที่มา


คำตอบ (1)


คุณสามารถสรุปคำจำกัดความของคลาส HavingField ได้ โดยเฉพาะอย่างยิ่ง คุณสามารถแสดงความสัมพันธ์ระหว่างตัวแปรประเภทที่อัปเดตและประเภทเรกคอร์ดโดยใช้การขึ้นต่อกันของฟังก์ชัน ซึ่งจะทำให้ตัวแปรประเภทการอัพเดตเกิดขึ้นในตำแหน่งใดก็ได้ และอนุญาตให้อัปเดตฟิลด์ monomorphic

class FieldC k k' x x' | k x' -> k', k' x -> k, k -> x, k' -> x' where
  fieldC :: Functor f => (x -> f x') -> k -> f k'

instance (b ~ b0, b' ~ b0') => FieldC (X1 a b) (X1 a b') b0 b0' where ...
instance (b ~ b0, b' ~ b0') => FieldC (X2 c a b) (X2 c a b') b0 b0' where ...

คุณกำหนดอินสแตนซ์ในลักษณะเดียวกันมาก โปรดทราบว่าข้อจำกัดความเท่าเทียมกันบางประการถูกวางไว้ในบริบทเพื่อปรับปรุงการอนุมานประเภท คุณสามารถอ่านตัวอย่างแรกด้านบนเป็น instance FieldC (X1 a b) (X1 a b') b b'

คลาสสำหรับฟิลด์อื่นถูกกำหนดในลักษณะเดียวกันทุกประการ นี่เป็นวิธีทั่วไปที่สุดในการกำหนดคลาสสำหรับเลนส์ (ซึ่งควรจะชัดเจนกว่านี้หากใครสังเกตว่าประเภทของ fieldC จริงๆ แล้วเป็นเพียง Lens k k' x x')

class FieldA k k' x x' | k x' -> k', k' x -> k, k -> x, k' -> x' where
  fieldA :: Functor f => (x -> f x') -> k -> f k'

class FieldB k k' x x' | k x' -> k', k' x -> k, k -> x, k' -> x' where
  fieldB :: Functor f => (x -> f x') -> k -> f k'

(หมายเหตุ: สิ่งนี้สามารถสรุปเป็นคลาสเดียวได้เช่นกันโดยมีพารามิเตอร์เพิ่มเติมที่สอดคล้องกับชื่อฟิลด์ นี่อาจอยู่นอกขอบเขตของคำถามนี้)

ตอนนี้ควรชัดเจนยิ่งขึ้นว่าจะเขียนการประกาศอินสแตนซ์อย่างไร:

instance (x0 ~ Int, x1 ~ Int) => FieldB (X1 a c) (X1 a c) x0 x1 where ...
instance (b0 ~ b, b0' ~ b') => FieldB (X2 a b c) (X2 a b' c) b0 b0' where ...

instance (a ~ a0, a' ~ a0') => FieldA (X1 a c) (X1 a' c) a0 a0' where ...
instance (a0 ~ a, a0' ~ a') => FieldA (X2 a b c) (X2 a' b c) a0 a0' where ...

ข้อแตกต่างเพียงอย่างเดียวสำหรับฟิลด์ monomorphic คือประเภทฟิลด์เป็นประเภท monomorphic

การทดสอบอย่างง่ายจะแสดงให้เห็นว่ามีการกำหนดประเภทโพลีมอร์ฟิกที่เหมาะสม:

foo x = 
  let y = view fieldB x
  in set fieldA (2 * y) $ set fieldC (3 + y) x

คุณสามารถขอ GHCi สำหรับประเภทที่อนุมานได้ในอินสแตนซ์เฉพาะ:

\x -> foo x `asTypeOf` X1{} :: X1 a b -> X1 Int Int
\x -> foo x `asTypeOf` X2{} :: Num a0' => X2 a a0' b -> X2 a0' a0' a0'

รูปแบบทั่วไปนี้สามารถนำไปใช้ได้เช่น ที่นี่. การใช้งานนี้ได้รับอนุญาตมากกว่าเล็กน้อย f ใน Functor f => .. เป็นพารามิเตอร์ประเภทคลาสแทนที่จะถูกวัดปริมาณแบบสากล ขึ้นอยู่กับกรณีการใช้งานเฉพาะของคุณ สิ่งนี้อาจจะใช่หรือไม่ก็ได้สำหรับคุณ

person user2407038    schedule 13.11.2017
comment
มีอะไรเข้มงวดกว่าอะไร? ยังไง? ฉันขาดอะไรบางอย่าง - person dfeuer; 14.11.2017
comment
@dfeuer ฉันไม่แน่ใจว่าฉันเขียนอะไรไปที่นั่น .. หวังว่าการแก้ไขจะสมเหตุสมผลกว่านี้ ขอบคุณที่เป็นนักอ่านที่ขยันเช่นนี้! - person user2407038; 14.11.2017
comment
ฉันจำเป็นต้องมี UndecidableInstances สำหรับสิ่งนี้หรือไม่ - person Vanson Samuel; 14.11.2017
comment
@VansonSamuel ฉันคิดอย่างนั้น (เพื่อให้มีข้อจำกัดด้านความเท่าเทียมกันของประเภทในบริบท - ด้วยเวอร์ชันที่ง่ายกว่าคือ instance FieldC (X1 a b) (X1 a b') b b' คุณไม่จำเป็นต้องใช้มัน) - person user2407038; 14.11.2017
comment
@ user2407038 ฉันโพสต์คำถามอื่นเกี่ยวกับวิธีการสรุปทั่วไปที่คุณพูดถึง: stackoverflow.com/questions/47401763/ ชอบที่จะเห็นคำตอบของคุณอย่างต่อเนื่อง - person Vanson Samuel; 21.11.2017