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 ExistentialQuantification
uzantı 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 exists
anahtar 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 a
ve 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 Typeable
veya Data
kullanmak 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 Typeable
veya 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 Worker
olması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 :Worker
Buffer
Worker
Buffer
MemoryBuffer
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 Worker
bağımsız değişken olarak alan bir işlev yazarsanız , yalnızca genel Buffer
tür sınıf özelliklerini (örneğin işlev output
) kullanabilir:
doWork :: Worker Int -> IO ()
doWork (Worker b x) = output (show x) b
b
Desen 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 Worker
tür, arabellek ve girdi için alanlara ek olarak, Buffer
sözlüğe işaret eden görünmez bir örtülü alana da sahiptir (v-tablosuna benzer, ancak uygun output
iş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 doWork
varoluş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 Worker
tü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
.