GADT'ler, örtük forall'lar sağlayarak Varoluşçu Türleri kullanarak kodlama için açık ve daha iyi sözdizimi sağlar
Bence GADT sözdiziminin daha iyi olduğuna dair genel bir anlaşma var. Bunun nedeni GADT'lerin örtük forall'ler sağlaması değil, bunun yerine ExistentialQuantificationuzantı ile etkinleştirilen orijinal sözdiziminin potansiyel olarak kafa karıştırıcı / yanıltıcı olması nedeniyle olduğunu söyleyemem . Bu sözdizimi elbette şuna benzer:
data SomeType = forall a. SomeType a
veya bir kısıtlamayla:
data SomeShowableType = forall a. Show a => SomeShowableType a
ve fikir birliği, buradaki anahtar kelimenin kullanımının forall, türün tamamen farklı türle kolayca karıştırılmasına izin verdiğini düşünüyorum:
data AnyType = AnyType (forall a. a) -- need RankNTypes extension
Daha iyi bir sözdizimi ayrı bir existsanahtar kelime kullanmış olabilir , bu nedenle şunu yazarsınız:
data SomeType = SomeType (exists a. a) -- not valid GHC syntax
GADT sözdizimi, örtülü veya açık olarak kullanılsa da forall, bu türler arasında daha eşittir ve anlaşılması daha kolay görünmektedir. Açık bir şekilde bile olsa forall, aşağıdaki tanım herhangi bir türden bir değer alabileceğiniz ave monomorfik içine koyabileceğiniz fikrine rastlar SomeType':
data SomeType' where
SomeType' :: forall a. (a -> SomeType') -- parentheses optional
ve bu tür ile aşağıdakiler arasındaki farkı görmek ve anlamak kolaydır:
data AnyType' where
AnyType' :: (forall a. a) -> AnyType'
Varoluşçu Türler içerdikleri türle ilgilenmiyor gibi gözükmektedir, ancak bunlarla eşleşen desen, Typeable veya Data kullanana kadar ne tür olduğunu bilmediğimiz bir tür olduğunu söylüyor.
Türleri Gizlemek istediğimizde kullanırız (örn: Heterojen Listeler için) veya Derleme Süresinde türlerin ne olduğunu gerçekten bilmiyoruz.
Varoluş türlerini kullanmak Typeableveya Datakullanmak zorunda olmasanız da , bunların çok uzakta olmadığını düşünüyorum . Varoluşçu bir tipin, belirtilmemiş bir tip etrafında iyi yazılmış bir "kutu" sağladığını söylemek daha doğru olur diye düşünüyorum. Kutu, türü bir anlamda "gizler", bu da içerdikleri türleri yok sayarak bu tür kutuların heterojen bir listesini yapmanıza olanak tanır. SomeType'Yukarıda olduğu gibi kısıtsız bir varoluşun oldukça işe yaramaz olduğu, ancak kısıtlı bir tip olduğu ortaya çıkıyor:
data SomeShowableType' where
SomeShowableType' :: forall a. (Show a) => a -> SomeShowableType'
"kutu" içine bakmak için eşleme deseni oluşturmanıza ve tip sınıfı olanaklarını kullanılabilir hale getirmenize olanak tanır:
showIt :: SomeShowableType' -> String
showIt (SomeShowableType' x) = show x
Bunun yalnızca Typeableveya değil, herhangi bir tür sınıfı için çalıştığını unutmayın Data.
Slayt destesinin 20. sayfasındaki kafa karışıklığınızla ilgili olarak, yazar varoluşsal bir işlevin belirli bir örneğe sahip Workerolmasını talep etmenin imkansız olduğunu söylüyor . Aşağıdaki gibi belirli bir tür kullanarak bir işlev oluşturmak için bir işlev yazabilirsiniz :WorkerBufferWorkerBufferMemoryBuffer
class Buffer b where
output :: String -> b -> IO ()
data Worker x = forall b. Buffer b => Worker {buffer :: b, input :: x}
data MemoryBuffer = MemoryBuffer
instance Buffer MemoryBuffer
memoryWorker = Worker MemoryBuffer (1 :: Int)
memoryWorker :: Worker Int
ancak Workerbağımsız değişken olarak alan bir işlev yazarsanız , yalnızca genel Buffertür sınıf özelliklerini (örneğin işlev output) kullanabilir:
doWork :: Worker Int -> IO ()
doWork (Worker b x) = output (show x) b
bDesen eşleştirme yoluyla bile, belirli bir tampon türü olmasını talep etmeye çalışamaz :
doWorkBroken :: Worker Int -> IO ()
doWorkBroken (Worker b x) = case b of
MemoryBuffer -> error "try this" -- type error
_ -> error "try that"
Son olarak, varoluşçu türlerle ilgili çalışma zamanı bilgileri, dahil olan tip sınıfları için örtük "sözlük" bağımsız değişkenleri aracılığıyla sağlanır. Yukarıdaki Workertür, arabellek ve girdi için alanlara ek olarak, Buffersözlüğe işaret eden görünmez bir örtülü alana da sahiptir (v-tablosuna benzer, ancak uygun outputişleve bir işaretçi içerdiğinden, çok büyük olmasına rağmen ).
Dahili olarak, type sınıfı Buffer, işlev alanlarına sahip bir veri türü olarak temsil edilir ve örnekler, bu türde "sözlüklerdir":
data Buffer' b = Buffer' { output' :: String -> b -> IO () }
dBuffer_MemoryBuffer :: Buffer' MemoryBuffer
dBuffer_MemoryBuffer = Buffer' { output' = undefined }
Varoluşçu türün bu sözlük için gizli bir alanı vardır:
data Worker' x = forall b. Worker' { dBuffer :: Buffer' b, buffer' :: b, input' :: x }
ve doWorkvaroluşsal Worker'değerler üzerinde çalışan böyle bir işlev şu şekilde uygulanır:
doWork' :: Worker' Int -> IO ()
doWork' (Worker' dBuf b x) = output' dBuf (show x) b
Yalnızca bir işlevi olan bir yazım sınıfı için sözlük aslında bir yeni tür için en iyi duruma getirilmiştir, bu nedenle bu örnekte varoluşçu Workertürü output, arabellek işlevine bir işlev işaretçisi içeren gizli bir alan içerir ve gereken tek çalışma zamanı bilgisi budur. tarafından doWork.