Saya baru saja memposting pertanyaan tentang sintaksis-2.0 tentang definisi share
. Saya sudah menjalankan ini di GHC 7.6 :
{-# LANGUAGE GADTs, TypeOperators, FlexibleContexts #-}
import Data.Syntactic
import Data.Syntactic.Sugar.BindingT
data Let a where
Let :: Let (a :-> (a -> b) :-> Full b)
share :: (Let :<: sup,
sup ~ Domain b, sup ~ Domain a,
Syntactic a, Syntactic b,
Syntactic (a -> b),
SyntacticN (a -> (a -> b) -> b)
fi)
=> a -> (a -> b) -> b
share = sugarSym Let
Namun, GHC 7.8 ingin -XAllowAmbiguousTypes
mengkompilasi dengan tanda tangan itu. Atau, saya bisa mengganti fi
dengan
(ASTF sup (Internal a) -> AST sup ((Internal a) :-> Full (Internal b)) -> ASTF sup (Internal b))
yang merupakan jenis yang tersirat oleh fundep pada SyntacticN
. Ini memungkinkan saya untuk menghindari ekstensi. Tentu ini
- tipe yang sangat panjang untuk ditambahkan ke tanda tangan yang sudah besar
- melelahkan untuk diturunkan secara manual
- tidak perlu karena fundep
Pertanyaan saya adalah:
- Apakah ini penggunaan yang dapat diterima
-XAllowAmbiguousTypes
? - Secara umum, kapan ekstensi ini digunakan? Jawaban di sini menunjukkan "itu hampir tidak pernah merupakan ide yang baik".
Meskipun saya sudah membaca dokumen , saya masih kesulitan memutuskan apakah suatu kendala ambigu atau tidak. Secara khusus, pertimbangkan fungsi ini dari Data.Syntactic.Sugar:
sugarSym :: (sub :<: AST sup, ApplySym sig fi sup, SyntacticN f fi) => sub sig -> f sugarSym = sugarN . appSym
Tampak bagi saya bahwa
fi
(dan mungkinsup
) harus ambigu di sini, tetapi dikompilasi tanpa ekstensi. Mengapa tidaksugarSym
ambigushare
? Karenashare
merupakan aplikasi darisugarSym
,share
semua kendala datang langsung darisugarSym
.
sugarSym Let
, yang merupakan(SyntacticN f (ASTF sup a -> ASTF sup (a -> b) -> ASTF sup b), Let :<: sup) => f
dan tidak melibatkan variabel tipe ambigu?share
, tetapi tidak kompilasi ketika salah satu dari tanda tangan yang disebutkan dalam pertanyaan digunakan. Pertanyaan Anda juga ditanyakan dalam komentar di posting sebelumnyaf
saja sudah cukup untuk sepenuhnya disambiguatesig
,fi
, dansup
.SyntacticN
merekfi
ambigu dalamsugarSym
, tapi kemudian mengapa hal yang sama tidak berlaku untukfi
dishare
?Jawaban:
Saya tidak melihat versi sintaksis yang diterbitkan yang tanda tangannya untuk
sugarSym
menggunakan nama-nama jenis yang tepat, jadi saya akan menggunakan cabang pengembangan di commit 8cfd02 ^ , versi terakhir yang masih menggunakan nama-nama itu.Jadi, mengapa GHC mengeluh tentang
fi
tanda tangan tipe Anda tetapi bukan untuksugarSym
? Dokumentasi yang Anda tautkan menjelaskan bahwa suatu tipe ambigu jika tidak muncul di sebelah kanan kendala, kecuali jika kendala tersebut menggunakan dependensi fungsional untuk menyimpulkan tipe yang ambigu dari tipe non-ambigu lainnya. Jadi mari kita bandingkan konteks kedua fungsi dan mencari dependensi fungsional.Jadi untuk
sugarSym
, tipe-tipe non-ambigu adalahsub
,sig
danf
, dan dari tipe-tipe tersebut kita harus dapat mengikuti dependensi fungsional untuk mendisambiguasikan semua tipe lain yang digunakan dalam konteks, yaitusup
danfi
. Dan memang,f -> internal
ketergantungan fungsional dalamSyntacticN
menggunakan kitaf
untuk disambiguasi kitafi
, dan kemudianf -> sig sym
ketergantungan fungsional dalamApplySym
menggunakan kita yang baru-disambiguasifi
untuk disambiguasisup
(dansig
, yang sudah non-ambigu). Jadi itu menjelaskan mengapasugarSym
tidak memerlukanAllowAmbiguousTypes
ekstensi.Sekarang mari kita lihat
sugar
. Hal pertama yang saya perhatikan adalah bahwa kompiler tidak mengeluh tentang tipe yang ambigu, melainkan tentang tumpang tindih contoh:Jadi, jika saya membaca ini dengan benar, bukan karena GHC berpikir bahwa tipe Anda ambigu, melainkan, saat memeriksa apakah tipe Anda ambigu, GHC menghadapi masalah yang berbeda dan terpisah. Ini kemudian memberi tahu Anda bahwa jika Anda memberi tahu GHC untuk tidak melakukan pemeriksaan ambiguitas, itu tidak akan menemui masalah yang terpisah. Ini menjelaskan mengapa mengaktifkan AllowAmbiguousTypes memungkinkan kode Anda dikompilasi.
Namun, masalah dengan instance yang tumpang tindih tetap ada. Dua contoh yang terdaftar oleh GHC (
SyntacticN f fi
danSyntacticN (a -> f) ...
) saling tumpang tindih. Anehnya, sepertinya yang pertama harus tumpang tindih dengan contoh lain, yang mencurigakan. Dan apa[overlap ok]
artinya?Saya curiga Syntactic dikompilasi dengan OverlappingInstances. Dan melihat kodenya , memang begitu.
Bereksperimen sedikit, tampaknya GHC baik-baik saja dengan contoh yang tumpang tindih ketika jelas bahwa yang satu lebih ketat daripada yang lain:
Tetapi GHC tidak baik dengan kasus yang tumpang tindih ketika tidak ada yang jelas lebih cocok dari yang lain:
Jenis tanda tangan Anda gunakan
SyntacticN (a -> (a -> b) -> b) fi
, dan tidakSyntacticN f fi
jugaSyntacticN (a -> f) (AST sym (Full ia) -> fi)
lebih cocok daripada yang lain. Jika saya mengubah bagian dari tanda tangan tipe Anda menjadiSyntacticN a fi
atauSyntacticN (a -> (a -> b) -> b) (AST sym (Full ia) -> fi)
, GHC tidak lagi mengeluh tentang tumpang tindih.Jika saya adalah Anda, saya akan melihat definisi dari dua contoh yang mungkin dan menentukan apakah salah satu dari dua implementasi itu adalah yang Anda inginkan.
sumber
Saya telah menemukan bahwa
AllowAmbiguousTypes
sangat nyaman untuk digunakanTypeApplications
. Pertimbangkan fungsinatVal :: forall n proxy . KnownNat n => proxy n -> Integer
dari GHC.TypeLits .Untuk menggunakan fungsi ini, saya bisa menulis
natVal (Proxy::Proxy5)
. Gaya alternatif adalah untuk digunakanTypeApplications
:natVal @5 Proxy
. JenisProxy
disimpulkan oleh aplikasi jenis, dan itu menjengkelkan harus menulisnya setiap kali Anda meneleponnatVal
. Dengan demikian kita dapat mengaktifkanAmbiguousTypes
dan menulis:Namun, perhatikan bahwa setelah Anda ambigu, Anda tidak dapat kembali !
sumber