Haskell'de Varoluşçu Tiplere İlişkin Açıklama


10

Haskell'deki Varoluşçu türleri anlamaya çalışıyorum ve bir PDF ile karşılaştım http://www.ii.uni.wroc.pl/~dabi/courses/ZPF15/rlasocha/prezentacja.pdf

Lütfen şimdiye kadar sahip olduğum aşağıdaki anlayışlarımı düzeltin.

  • 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.
  • GADTVar örtülü sağlayarak Varolus türleri kullanarak kod açık ve daha iyi sözdizimi sağlar foralls '

Şüphelerim

  • Yukarıdaki PDF'nin 20'sinde aşağıdaki kod için bir Fonksiyonun spesifik Tampon talep etmesinin imkansız olduğu belirtilmiştir. Neden böyle? Bir Fonksiyon hazırlarken ne tür bir tampon kullanacağımı tam olarak biliyorum, buna rağmen hangi veriyi koyacağımı bilmiyor olabilirim. Sahip Olmanın Nesi Yanlış :: Worker MemoryBuffer IntGerçekten Tampon üzerinden soyut yapmak istiyorlarsa, Sum türüne sahip olabilirler data Buffer = MemoryBuffer | NetBuffer | RandomBufferve:: Worker Buffer Int
data Worker x = forall b. Buffer b => Worker {buffer :: b, input :: x}
data MemoryBuffer = MemoryBuffer

memoryWorker = Worker MemoryBuffer (1 :: Int)
memoryWorker :: Worker Int
  • Haskell, C gibi bir Tam Tip Silme dilidir, sonra Çalışma Zamanı'nda hangi işlevi çağıracağını nasıl bilir? Çok az bilgi tutacak ve Devasa bir V-Fonksiyonlar Tablosuna geçecek ve çalışma zamanında V-Table'dan çözecek gibi bir şey mi? Eğer öyleyse, ne tür bir bilgiyi depolayacak?

Yanıtlar:


8

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.


Varoluşçular Sıra 1 gibidir Veri beyanları için mi? Varoluşçular Haskell'de Sanal Fonksiyonları herhangi bir OOP dilinde olduğu gibi ele almanın bir yolu mu?
Pawan Kumar

1
Muhtemelen AnyTypebir rütbe-2 tipi aramamalıydım; bu sadece kafa karıştırıcı ve onu sildim. Yapıcı AnyTypebir sıra-2 işlevi gibi davranır ve yapıcı SomeTypebir sıra-1 işlevi (çoğu varoluşsal olmayan tür gibi) gibi davranır, ancak bu çok yararlı bir karakterizasyon değildir. Bir şey varsa, bu türleri ilginç kılan şey, nicelikli türleri "içermelerine" rağmen, kendilerinin 0 rütbeleri (yani, bir tür değişkeni üzerinde niceliklendirilmemiş ve bu yüzden monomorfik) olmalarıdır.
KA Buhr

1
Varoluşçu türler yerine tür sınıfları (ve özellikle yöntem işlevleri), sanal işlevlere muhtemelen en doğrudan Haskell eşdeğeridir. Teknik anlamda, OOP dillerinin sınıfları ve nesneleri varoluşsal türler ve değerler olarak görülebilir, ancak pratik olarak, Haskell'de OOP "sanal fonksiyon" polimorfizm tarzını uygulamak, toplam türleri, tip sınıflar ve / veya parametrik polimorfizm.
KA Buhr

4

Yukarıdaki PDF'nin 20'sinde aşağıdaki kod için bir Fonksiyonun spesifik Tampon talep etmesinin imkansız olduğu belirtilmiştir. Neden böyle?

Çünkü Worker, tanımlandığı gibi, yalnızca bir argüman alır, "girdi" alanının türü (tür değişkeni x). Örneğin Worker Intbir tiptir. Bunun byerine, type değişkeni bir parametre Workerdeğil, bir tür "yerel değişken" dir. İçinde olduğu gibi geçirilemez Worker Int String- bu bir tür hatasını tetikler.

Bunun yerine tanımlamış olsaydık:

data Worker x b = Worker {buffer :: b, input :: x}

o Worker Int Stringzaman işe yarar, ancak tür artık varoluşsal değildir - şimdi her zaman arabellek türünü de geçmeliyiz.

Haskell, C gibi bir Tam Tip Silme dilidir, sonra Çalışma Zamanı'nda hangi işlevi çağıracağını nasıl bilir? Çok az bilgi tutacak ve Devasa bir V-Fonksiyonlar Tablosuna geçecek ve çalışma zamanında V-Table'dan çözecek gibi bir şey mi? Eğer öyleyse, ne tür bir bilgiyi depolayacak?

Bu kabaca doğrudur. Kısacası Worker, yapıcıyı her uyguladığınızda , GHC, btürünün argümanlarından türü ihlal eder Workerve ardından bir örnek arar Buffer b. Bu bulunursa, GHC nesnedeki örneğe ek bir işaretçi ekler. En basit haliyle, sanal işlevler mevcut olduğunda OOP'deki her nesneye eklenen "göstericiden vtable'a" çok farklı değildir.

Genel durumda, çok daha karmaşık olabilir. Derleyici farklı bir gösterim kullanabilir ve kodu hızlandırırsa, tek bir temsilci (örneğin, tüm örnek yöntemlerine doğrudan işaretçiler ekleme) yerine daha fazla işaretçi ekleyebilir. Ayrıca, bazen derleyicinin bir kısıtlamayı karşılamak için birden çok örnek kullanması gerekir. Örneğin, örneğini Eq [Int]... için depolamamız gerekirse, o zaman bir değil iki tane vardır: biri için Intve biri listeler için ve ikisinin birleştirilmesi gerekir (çalışma zamanında, engelleme optimizasyonları).

GHC'nin her durumda tam olarak ne yaptığını tahmin etmek zordur: bu, tetikleyebilecek veya tetikleyemeyecek bir ton optimizasyona bağlıdır.

Neler olup bittiğini daha fazla görmek için tür sınıflarının "sözlük tabanlı" uygulaması için Google'ı deneyebilirsiniz. Ayrıca GHC'den dahili optimize edilmiş Çekirdeği yazdırmasını isteyebilir -ddump-simplve oluşturulmuş, depolanmış ve etrafından aktarılan sözlükleri gözlemleyebilirsiniz. Sizi uyarmalıyım: Çekirdek oldukça düşük seviyededir ve ilk başta okunması zor olabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.