Membuat lensa polimorfik

Saya dapat membuat lensa untuk bidang terakhir (c) di tipe data saya dengan melakukan hal berikut:

{-# 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, ..})

Apakah ada hal serupa yang bisa saya lakukan untuk bidang a dan b


person Vanson Samuel    schedule 13.11.2017    source sumber


Jawaban (1)


Anda dapat menggeneralisasi definisi kelas HavingField; khususnya, Anda dapat mengekspresikan hubungan antara variabel tipe yang diperbarui dan tipe rekaman menggunakan dependensi fungsional. Hal ini memungkinkan variabel tipe pembaruan terjadi di posisi mana pun; dan itu memungkinkan pembaruan pada bidang monomorfik.

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 ...

Anda mendefinisikan instance dengan cara yang hampir sama; perhatikan bahwa beberapa batasan kesetaraan ditempatkan dalam konteks untuk meningkatkan inferensi tipe. Anda dapat membaca contoh pertama di atas sebagai instance FieldC (X1 a b) (X1 a b') b b'.

Kelas untuk bidang lain didefinisikan dengan cara yang persis sama; ini pada dasarnya adalah cara paling umum untuk mendefinisikan kelas lensa (yang akan lebih jelas jika diketahui bahwa tipe fieldC sebenarnya hanya 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'

(Catatan: ini juga dapat digeneralisasikan ke satu kelas dengan parameter tambahan yang sesuai dengan nama bidang; ini mungkin di luar cakupan pertanyaan ini).

Sekarang seharusnya lebih jelas cara menulis deklarasi instance:

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 ...

Satu-satunya perbedaan untuk bidang monomorfik adalah bahwa tipe bidangnya adalah tipe monomorfik.

Tes sederhana akan menunjukkan bahwa tipe polimorfik yang tepat telah ditetapkan:

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

Anda dapat menanyakan tipe yang disimpulkan pada GHCI pada contoh tertentu:

\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'

Pola umum ini dapat ditemukan diterapkan misalnya. di sini. Penerapan ini sedikit lebih permisif; f di Functor f => .. adalah parameter kelas tipe alih-alih diukur secara universal. Bergantung pada kasus penggunaan spesifik Anda, ini mungkin berhasil atau tidak.

person user2407038    schedule 13.11.2017
comment
Apa yang lebih membatasi dari apa? Bagaimana? Saya melewatkan sesuatu. - person dfeuer; 14.11.2017
comment
@dfeuer Saya tidak yakin apa yang saya tulis di sana.. semoga hasil editnya lebih masuk akal. Terima kasih telah menjadi pembaca yang rajin! - person user2407038; 14.11.2017
comment
Apakah saya memerlukan UndecidableInstances untuk ini? - person Vanson Samuel; 14.11.2017
comment
@VansonSamuel Saya rasa begitu (untuk mendapatkan batasan kesetaraan tipe dalam konteksnya - dengan versi yang lebih sederhana, yaitu instance FieldC (X1 a b) (X1 a b') b b', Anda tidak memerlukannya). - person user2407038; 14.11.2017
comment
@ user2407038 Saya memposting pertanyaan lain tentang cara melakukan generalisasi yang Anda sebutkan: stackoverflow.com/questions/47401763/. Ingin sekali melihat kelanjutan jawaban Anda. - person Vanson Samuel; 21.11.2017