Neden bağımlı olarak yazılmasın?


161

Birkaç kaynak gördüm "Haskell yavaş yavaş bağımlı-tipli bir dil haline geliyor" görüş. Bunun anlamı, gittikçe daha fazla dil uzantısıyla, Haskell'in bu genel yönde sürüklendiği, ancak henüz orada olmadığı anlamına geliyor.

Temel olarak bilmek istediğim iki şey var. Birincisi, oldukça basit bir şekilde, "bağımlı tipte bir dil olmak" aslında ne anlama geliyor ? (Umarım bu konuda çok teknik olmadan.)

İkinci soru ... dezavantajı nedir? Yani, insanlar bu yöne gittiğimizi biliyorlar, bu yüzden bunun bir avantajı olmalı. Ve henüz, henüz orada değiliz, bu yüzden insanların sonuna kadar gitmesini engelleyen bazı dezavantajlar olmalı. Sorunun karmaşıklıkta dik bir artış olduğu izlenimini edindim. Ancak, bağımlı yazmanın ne olduğunu gerçekten anlamadım, emin değilim.

Ne yapmak biliyorum ben bağımlı yazılan programlama dili hakkında okumaya başlamak her zaman, metin sorun olduğunu Tahminen ... tamamen anlaşılmaz olduğudur. (?)


10
Basitçe söylemek gerekirse, terimlere bağlı olan türler yazabilirsiniz (hesaplamalar). Bu, programınızın her yönü hakkında türleri belirtmek için yeterlidir ve bu nedenle, tür sisteminin tam program belirtimine sahip olabileceği anlamına gelir. Sorun, türlerin hesaplamalara bağlı olması nedeniyle, tür denetiminin yapılması çok daha zordur (genel olarak imkansız).
GManNickG

27
@GManNickG: Tip kontrolü tamamen mümkündür. Tür çıkarımı başka bir konudur, ancak daha sonra GHC'nin çeşitli uzantıları, tüm türleri çıkarmanın mümkün olması gerektiği fikrini terk etti.
CA McCann

7
Doğru anlarsam, dezavantaj, bağımlı yazmayı doğru yapmanın (örneğin, hem kullanılabilir hem de iyi kurulmuş bir şekilde) zor olmasıdır ve henüz ne kadar olduğunu bilmiyoruz.
comingstorm

1
@CAMcCann: Evet, hatam.
GManNickG

4
Kimsenin büyük bir pragmatik dezavantaja işaret ettiğini sanmıyorum: tüm kodunuzun doğru olduğuna dair kanıtlar yazmak oldukça çılgınca sıkıcı. Yazım çıkarımını otomatik olarak yapamayacağınız için ("hella güçlü" mantığında kanıtlanan teoremlere karşılık gelir), programınız için kanıtlar şeklinde ek açıklamalar yazmanız gerekir. Bu, özellikle insanların genellikle Haskell'de yaptıkları daha ayrıntılı monadik sihir için bir süre sonra can sıkıcı ve zorlaşıyor. Bu günlerde en yakın geleceğimiz, bunun çoğunu bizim için yapan veya bize iyi bir ilkel kitle veren dillerdir.
Kristopher Micinski

Yanıtlar:


21

Bağımlı yazım gerçekten sadece değer ve tür seviyelerinin birleştirilmesidir, bu nedenle türler üzerindeki değerleri parametreleştirebilirsiniz (Haskell'de tip sınıfları ve parametrik polimorfizm ile zaten mümkündür) ve değerler üzerindeki türleri parametrelendirebilir (kesin olarak değil, henüz Haskell'de mümkündür) , DataKindsçok yakın olmasına rağmen ).

Düzenleme: Görünüşe göre, bu noktadan itibaren, yanılmışım (bakınız @ pigworker yorum). Bunun geri kalanını, beslendiğim mitlerin bir kaydı olarak koruyacağım. : P


Duyduğumdan tam bağımlı yazmaya geçmeyle ilgili sorun, Haskell'in silinmiş türlerle verimli makine koduna derlenmesini sağlayan tür ve değer seviyeleri arasındaki faz kısıtlamasını bozmasıdır. Mevcut teknoloji seviyemizle, bağımlı olarak yazılan bir dil bir noktada bir tercümandan geçmelidir (hemen veya bağımlı olarak yazılan bayt koduna veya benzerine derlendikten sonra).

Bu mutlaka temel bir kısıtlama değildir, ancak bu konuda ümit verici görünen ancak bunu zaten GHC'ye dönüştürmemiş olan herhangi bir araştırmanın kişisel olarak farkında değilim. Başka biri daha fazlasını bilirse, düzeltildiğim için mutlu olurum.


46
Söyledikleriniz neredeyse tamamen yanlış. Seni tamamen suçlamıyorum: standart mitleri gerçek olarak tekrarlıyor. Edwin Brady'nin dili Idris, tür silme gerçekleştirir (çünkü çalışma zamanı davranışı türlere bağlı değildir) ve stok G-makine teknikleri kullanılarak kodun oluşturulduğu kodlama yapan oldukça standart bir lambda-kaldırılmış süper birleştirici üretir.
pigworker

3
Bir yan not olarak, birisi yakın zamanda beni bu makaleye işaret etti . Söyleyebileceğim kadarıyla, Haskell'i bağımlı bir şekilde türlendirirdi (yani, tip seviyesi dili bağımlı olarak yazılır), bu da yakında istediğimizi görebildiğim kadar yakın.
Ptharien's Flame

8
Evet, bu makale türlerin tür düzeyindeki öğelere nasıl bağımlı hale getirileceğini (ve tür / tür ayrımını ortadan kaldırdığını) göstermenin yolunu buluyor. Zaten tartışılmakta olan makul bir takip, gerçek bağımlı işlev türlerine izin vermek, ancak argümanlarını hem değer hem de tür katmanlarında bulunabilecek dil parçasıyla sınırlamaktır (şimdi veri türü tanıtımı sayesinde önemsiz değildir). Bu, şu anda "sahteciliği" istenenden daha karmaşık hale getiren singleton inşaatına olan ihtiyacı ortadan kaldıracaktır. Gerçek olana sürekli yaklaşıyoruz.
pigworker

13
Haskell'e bağımlı türleri uyarlayan birçok pragmatik soru var. Bağımlı fonksiyon alanının bu sınırlı biçimini elde ettikten sonra, hala tip düzeyinde izin verilen değer dilinin parçasını nasıl genişleteceğimize ve denklem teorisinin ne olması gerektiği sorusuyla yüzleşiriz (2 + 2'nin 4, vb.). Sıfırdan bağımlı olarak yazılan diller tasarımını başlangıçtan uzaklaştıran pek çok meseleci (örneğin altta) vardır.
pigworker

2
@pigworker Haskell'in temiz bir alt kümesi var mı? Eğer öyleyse, bunu sadece "değer ve tip katmanlarında var olan dilin parçası" için kullanamaz mıydık? Değilse, bir tane üretmek için ne gerekir?
Ptharien's Flame

223

Bağımlıca Yazılan Haskell, Şimdi?

Haskell, bir dereceye kadar, bağımlı olarak yazılmış bir dildir. Tip seviyesi veri kavramı var, şimdi sayesinde daha mantıklı bir şekilde yazılıyorDataKindsGADTs çalışma zamanı temsili vermek için bazı araçlar ( ) vardır. Bu nedenle, çalışma zamanı öğelerinin değerleri türlerde etkili bir şekilde ortaya çıkar, bu da bir dilin bağımlı olarak yazılmasının anlamıdır.

Basit veri türleri , tür düzeyine yükseltilir , böylece içerdikleri değerler türlerde kullanılabilir. Bu nedenle arketip örneği

data Nat = Z | S Nat

data Vec :: Nat -> * -> * where
  VNil   :: Vec Z x
  VCons  :: x -> Vec n x -> Vec (S n) x

mümkün olur ve bununla birlikte,

vApply :: Vec n (s -> t) -> Vec n s -> Vec n t
vApply VNil         VNil         = VNil
vApply (VCons f fs) (VCons s ss) = VCons (f s) (vApply fs ss)

ki bu güzel. Uzunluğun nbu işlevde tamamen statik bir şey olduğunu ve giriş ve çıkış vektörlerinin aynı uzunlukta olmasını sağladığına dikkat edin, bu uzunluk, yürütülmesinde hiçbir rol oynamaz vApply. Buna karşılık, o yapar işlevi uygulamak için (yani imkansız) çok daha yanıltıcıdır nverilen bir kopyalarını x(olacağını purehiç vApplys' <*>)

vReplicate :: x -> Vec n x

çünkü çalışma zamanında kaç kopya çekileceğini bilmek çok önemlidir. Tek tonları girin.

data Natty :: Nat -> * where
  Zy :: Natty Z
  Sy :: Natty n -> Natty (S n)

Herhangi bir promotable türü için, değerlerinin çalışma zamanı kopyaları tarafından ikamet edilen, yükseltilen tür üzerinde endekslenen singleton ailesini oluşturabiliriz. Natty ntür seviyesinin çalışma zamanı kopyalarının türüdür n :: Nat. Şimdi yazabiliriz

vReplicate :: Natty n -> x -> Vec n x
vReplicate Zy     x = VNil
vReplicate (Sy n) x = VCons x (vReplicate n x)

Böylece, çalışma zamanı değerine sabitlenmiş bir tür düzeyi değeriniz vardır: çalışma zamanı kopyasının incelenmesi, tür düzeyi değerinin statik bilgisini geliştirir. Terimler ve türler ayrılsa da, singleton yapısını bir tür epoksi reçinesi olarak kullanarak fazlar arasında bağlar oluşturarak bağımlı tipte bir şekilde çalışabiliriz. Bu, türlerde rasgele çalışma zamanı ifadelerine izin vermenin çok uzun bir yoludur, ancak hiçbir şey değildir.

Nasty nedir? Ne kayıp?

Bu teknolojiye biraz baskı uygulayalım ve neyin sallanmaya başladığını görelim. Tekiltonların biraz daha dolaylı olarak yönetilebilir olması gerektiği fikrini alabiliriz

class Nattily (n :: Nat) where
  natty :: Natty n
instance Nattily Z where
  natty = Zy
instance Nattily n => Nattily (S n) where
  natty = Sy natty

yazmamıza izin verin,

instance Nattily n => Applicative (Vec n) where
  pure = vReplicate natty
  (<*>) = vApply

Bu işe yarıyor, ama şimdi orijinal Nattipimizin üç kopya oluşturduğu anlamına geliyor : bir tür, bir tek aile ve bir tek sınıf. Müstehcen Natty ndeğerler ve Nattily nsözlükler alışverişinde bulunmak için oldukça karmaşık bir sürecimiz var . Dahası, Nattydeğil Nat: çalışma zamanı değerlerine bir çeşit bağımlılığımız var, ama ilk düşündüğümüz tipte değil. Tamamen bağımlı olarak yazılan hiçbir dil bağımlı türleri bu kadar karmaşık hale getirmez

Bu arada, Natterfi edilebilir , ancak olamaz Vec. Dizine alınmış bir türe göre dizine ekleyemezsiniz. Bağımlı olarak yazılan diller üzerinde tam bir kısıtlama yoktur ve kariyerime bağımlı olarak yazılan bir gösteriş olarak, sadece tek katmanlı dizinleme yapan kişilere öğretmek için görüşmelerime iki katmanlı dizinleme örnekleri eklemeyi öğrendim. bir kart evi gibi katlanmamı beklememek zor ama mümkün. Sorun ne? Eşitlik. GADT'ler, bir kurucuya belirli bir dönüş türü verirken dolaylı olarak elde ettiğiniz kısıtlamaları açık denklem taleplerine çevirerek çalışır. Bunun gibi.

data Vec (n :: Nat) (x :: *)
  = n ~ Z => VNil
  | forall m. n ~ S m => VCons x (Vec m x)

İki denklemimizin her birinde, her iki tarafın da nazik Nat .

Şimdi aynı çeviriyi vektörler üzerinden dizine eklenmiş bir şey için deneyin.

data InVec :: x -> Vec n x -> * where
  Here :: InVec z (VCons z zs)
  After :: InVec z ys -> InVec z (VCons y ys)

olur

data InVec (a :: x) (as :: Vec n x)
  = forall m z (zs :: Vec x m). (n ~ S m, as ~ VCons z zs) => Here
  | forall m y z (ys :: Vec x m). (n ~ S m, as ~ VCons y ys) => After (InVec z ys)

ve şimdi as :: Vec n xve arasında eşitlik kısıtlamaları oluşturuyoruzVCons z zs :: Vec (S m) x iki tarafın sözdizimsel olarak farklı (ama muhtemelen eşit) türler olduğu yerde . GHC çekirdeği şu anda böyle bir konsept için donatılmamıştır!

Başka ne eksik? Eh, Haskell çoğu tür düzeyinde eksik. Terfi edebileceğiniz terimlerin dili gerçekten sadece değişkenlere ve GADT dışı kuruculara sahiptir. Bunlara sahip olduğunuzda, type familymakine tip seviyesi programlar yazmanıza izin verir: bunlardan bazıları, terim düzeyinde yazmayı düşündüğünüz fonksiyonlara benzeyebilir (örneğin, Natekleme ile donatmak , böylece eklemek için iyi bir tür verebilirsiniz Vec) , ama bu sadece bir tesadüf!

Uygulamada eksik olan bir diğer şey de türleri değerlere göre endekslemek için yeni yeteneklerimizden yararlanan bir kütüphanedir . Bu cesur yeni dünyada ne olur Functor ve ne Monadolur? Bunu düşünüyorum, ama daha yapacak çok şey var.

Tür Düzeyinde Programları Çalıştırma

Haskell, çoğu bağımlı tipteki programlama dili gibi, iki işlevsel semantiğe sahiptir. Çalışma zamanı sisteminin programları çalıştırma şekli vardır (yalnızca kapalı ifadeler, tür silindikten sonra, son derece optimize edilmiştir) ve sonra daktilo makinesinin programları çalıştırma şekli vardır (tip aileleriniz, "tip sınıf Prolog", açık ifadeler ile). Haskell için normalde ikisini karıştırmazsınız, çünkü yürütülen programlar farklı dillerdedir. Bağımlı olarak yazılan diller aynı program dili için ayrı çalışma zamanı ve statik yürütme modellerine sahiptir , ancak endişelenmeyin, çalışma zamanı modeli yine de yazım silmenizi ve gerçekten de kanıt silmenizi sağlar: Coq'un çıkarımı budurmekanizma size verir; en azından Edwin Brady'nin derleyicisinin yaptığı şey (Edwin gereksiz yere yinelenen değerleri, türleri ve kanıtları siler). Faz ayrımı artık sözdizimsel kategorinin bir ayrımı olmayabilir , ama canlı ve iyidir.

Bağımlı olarak yazılan diller, toplam olarak, daktilo makinesinin programları uzun bir bekleyişten daha kötü bir şey korkusundan uzak çalıştırmasına izin verir. Haskell daha bağımlı bir şekilde yazıldıkça, statik yürütme modelinin ne olması gerektiği sorusuyla karşı karşıyayız? Bir yaklaşım, statik yürütmeyi toplam işlevlerle sınırlamak olabilir, bu da bize aynı özgürlüğü çalıştırmamıza izin verir, ancak bizi veri ve kodata arasında ayrım yapmaya (en azından tip seviyesi kodu) ayırmaya zorlayabilir, böylece fesih veya verimliliği zorunlu kılmak. Ama bu tek yaklaşım değil. Programları çalıştırmak istemeyen çok daha zayıf bir yürütme modeli seçmekte özgürüz, daha az denklemin sadece hesaplama ile ortaya çıkması pahasına. Ve aslında, GHC aslında bunu yapar. GHC çekirdeği için yazma kuralları çalıştırmadan programları, ancak sadece denklemler için kanıtları kontrol etmek için. Çekirdeğe çevirirken, GHC'nin kısıtlayıcı çözücüsü, belirli bir ifadenin normal biçimine eşit olduğuna dair küçük bir kanıt izi oluşturarak, tür düzeyindeki programlarınızı çalıştırmaya çalışır. Bu kanıt oluşturma yöntemi biraz tahmin edilemez ve kaçınılmaz olarak eksiktir: örneğin, korkutucu görünümlü özyineleme ile savaşır ve muhtemelen akıllıca olur. Endişelenmemiz gerekmeyen bir şey IO , daktiloda hesaplamaların yapılmasıdır: daktilo makinesinin launchMissilesçalışma zamanı sisteminin yaptığıyla aynı anlamı vermek zorunda olmadığını unutmayın !

Hindley-Milner Kültürü

Hindley-Milner tipi sistem, birçok insanın ayrımlar arasındaki ayrımı göremediği ve tesadüfün kaçınılmaz olduğunu varsaydığı talihsiz kültürel yan etki ile dört farklı ayrımın gerçekten müthiş tesadüfünü gerçekleştirir! Ne hakkında konuşuyorum?

  • şartlar ve türler
  • açıkça yazılmış şeyler vs üstü kapalı yazılmış şeyler
  • çalışma süresinde mevcudiyet vs silme
  • bağımlı olmayan soyutlama vs bağımlı niceleme

Terimler yazmaya ve türlerin çıkarılmaya bırakılmasına alışkınız ... ve sonra siliniriz. Sessiz ve statik olarak ilgili tip soyutlaması ve uygulaması ile tip değişkenlerini ölçmeye alışkınız.

Bu ayrımlar hizalanmadan önce vanilya Hindley-Milner'dan çok uzak durmak zorunda değilsiniz ve bu kötü bir şey değil . Başlangıç ​​olarak, bunları birkaç yere yazmak istiyorsak daha ilginç türlere sahip olabiliriz. Bu arada, aşırı yüklenmiş işlevleri kullandığımızda tip sınıfı sözlükler yazmak zorunda değiliz, ancak bu sözlükler çalışma zamanında kesinlikle mevcut (veya satır içi). Bağımlı olarak yazılan dillerde, çalışma zamanında yalnızca türlerden daha fazlasını silmeyi bekleriz, ancak (tür sınıflarında olduğu gibi) bazı örtük olarak çıkartılmış değerlerin silinmeyeceğini sileriz. Örneğin, vReplicatesayısal argüman genellikle istenen vektörün tipinden etkilenmez, ancak yine de çalışma zamanında bilmemiz gerekir.

Bu tesadüfler artık geçerli olmadığından hangi dil tasarım seçeneklerini gözden geçirmeliyiz? Örneğin, Haskell'in forall x. taçıkça bir nicelleştiriciyi somutlaştırmanın bir yolu olmadığı doğru mu? Daktilo unifiying ile tahmin xedemezse t, ne xolması gerektiğini söylemenin başka bir yolu yoktur .

Daha geniş anlamda, "tür çıkarımını" ya hepimizin ya da hiçbirimizin sahip olmadığı monolitik bir kavram olarak ele alamayız. Başlangıç ​​olarak, aptal bir makinenin birini tahmin edebilmesini sağlamak için hangi türlerin mevcut olduğunu kısıtlamaya dayanan "genelleme" yönünü (Milner'ın "izin" kuralı) ayırmamız gerekir (Milner'ın "var "kural) kısıtlama çözücünüz kadar etkili. Üst düzey türlerin çıkarımını zorlaştırmasını bekleyebiliriz, ancak iç tür bilgilerinin yayılması oldukça kolay kalacaktır.

Haskell İçin Sonraki Adımlar

Tür ve tür seviyelerinin çok benzer büyüdüğünü görüyoruz (ve zaten GHC'de dahili bir temsili paylaşıyorlar). Onları birleştirebiliriz. * :: *Yapabilirsek alması eğlenceli olurdu : uzun zaman önce, dibe izin verdiğimizde mantıksal sağlamlığı kaybettik , ancak tip sağlamlığı genellikle daha zayıf bir gereksinimdir. Kontrol etmeliyiz. Farklı tür, tür vb. Seviyelere sahip olmamız gerekiyorsa, en azından tür düzeyinde ve üstündeki her şeyin her zaman tanıtılabileceğinden emin olabiliriz. Tür seviyesindeki polimorfizmi yeniden icat etmek yerine, halihazırda sahip olduğumuz polimorfizmi yeniden kullanmak harika olurdu.

Biz basitleştirmek ve izin vererek kısıtlamalar mevcut sistemi genelleme gereken heterojen denklemleri a ~ bçeşitleri ave bsözdizimsel aynı olmayan (ama eşit kanıtlanabilir). Bağımlılıkla başa çıkmayı kolaylaştıran eski bir teknik (tezimde geçen yüzyıl). GADT'lerde ifadeler üzerindeki kısıtlamaları ifade edebilir ve böylece neyin tanıtılabileceğine dair kısıtlamaları gevşetebiliriz.

Bağımlı bir fonksiyon tipi getirerek singleton yapımına olan ihtiyacı ortadan kaldırmalıyız pi x :: s -> t. Bu tipte bir işlev, tür ve terim dillerinin kesişiminde yaşayan herhangi bir tür ifadesine açıkça uygulanabilir (yani, değişkenler, yapıcılar, daha sonra gelecek). İlgili lambda ve uygulama çalışma zamanında silinmez, bu yüzden yazabilirizs

vReplicate :: pi n :: Nat -> x -> Vec n x
vReplicate Z     x = VNil
vReplicate (S n) x = VCons x (vReplicate n x)

değiştirmeden Nattarafından Natty. Etki alanı piherhangi bir promotable tip olabilir, bu nedenle GADT'ler yükseltilebiliyorsa, bağımlı niceleyici diziler (veya de Briuijn'in dediği gibi "teleskoplar") yazabiliriz

pi n :: Nat -> pi xs :: Vec n x -> ...

ihtiyacımız olan uzunlukta.

Bu adımların amacı , zayıf araçlar ve karmaşık kodlamalar yapmak yerine doğrudan daha genel araçlarla çalışarak karmaşıklığı ortadan kaldırmaktır . Mevcut kısmi satın alma, Haskell'in bağımlı türlerinin faydalarını olması gerekenden daha pahalı hale getiriyor.

Çok zor?

Bağımlı tipler birçok insanı gerginleştirir. Beni sinirlendiriyorlar, ama gergin olmayı seviyorum, ya da en azından gergin olmamayı zor buluyorum. Ancak konunun etrafında böyle bir cehalet sisi olması yardımcı olmuyor. Bazıları hepimizin hala öğrenecek çok şeyi olması nedeniyle. Ancak daha az radikal yaklaşımların savunucularının, gerçeklerin tamamen onlarla olduğundan emin olmadan bağımlı türlerin korkusunu engellediği bilinmektedir. İsimleri adlandırmayacağım. Bu "kararsız daktilo denetimi", "Tamamlanmayan tur", "faz ayrımı yok", "tip silme yok", "her yerde kanıt", vb.

Bağımlı olarak yazılan programların her zaman doğru olduğu kesinlikle doğru değildir. Kişi, programlarının temel hijyenini geliştirebilir, tam bir spesifikasyona gitmeden türlerde ek değişmezleri zorlayabilir. Bu yöndeki küçük adımlar, çoğu zaman ek kanıt yükümlülüğü olmaksızın veya daha azıyla çok daha güçlü garantiler sağlar. Bağımlı olarak yazılan programların kaçınılmaz olarak kanıtlarla dolu olduğu doğru değildir , aslında genellikle tanımlarımı sorgulamak için kodumda herhangi bir kanıtın varlığını alırım .

Çünkü artikülasyondaki herhangi bir artışta olduğu gibi, faul yeni şeyler de adil olarak söyleyebiliriz. Örneğin, ikili arama ağaçlarını tanımlamak için birçok crummy yol vardır, ancak bu iyi bir yol olmadığı anlamına gelmez . Egoyu kabul etmeye zorlasa bile, kötü deneyimlerin iyileştirilemeyeceğini varsaymamak önemlidir. Bağımlı tanımların tasarımı öğrenmeyi gerektiren yeni bir beceridir ve Haskell programcısı olmak sizi otomatik olarak uzman yapmaz! Ve bazı programlar kötü olsa bile, neden başkalarına adil olma özgürlüğünü inkar edesiniz ki?

Neden Haskell ile Rahatsız Etmeliyim?

Bağımlı tiplerden gerçekten hoşlanıyorum, ancak bilgisayar korsanlığı projelerimin çoğu hala Haskell'de. Neden? Haskell'in tip sınıfları vardır. Haskell'in faydalı kütüphaneleri vardır. Haskell, efektlerle programlamanın uygulanabilir (ideal olmaktan uzak olsa da) tedavisine sahiptir. Haskell endüstriyel bir güç derleyicisine sahiptir. Bağımlı olarak yazılan diller, büyüyen topluluk ve altyapı konusunda çok daha erken bir aşamadadır, ancak oraya ulaşacağız, örneğin metaprogramlama ve veri türü jenerikleri gibi mümkün olan şeylerde gerçek bir nesil değişimi. Ancak, sadece mevcut dil neslini ileriye doğru iterek kazanılacak çok fayda olduğunu görmek için insanların Haskell'in bağımlı türlere yönelik adımlarının bir sonucu olarak neler yaptığını incelemelisiniz.


6
Henüz DataKinds ile ilgilenmiyorum. Çoğunlukla böyle bir şey yapmak istiyorum çünkü: fmap read getLine >>= \n -> vReplicate n 0. Belirttiğiniz gibi, Nattybundan uzak bir yol var. Ayrıca, vReplicate gerçek bir bellek dizi gibi bir çevrilebilir olması gerekir newtype SVector n x = SVector (Data.Vector.Vector x), nsorular Nat(veya benzeri). Belki de "bağımlı tipte bir gösteriş" için başka bir gösteri noktası olabilir.
John L

7
Efektlerle programlamanın ideal tedavisi için aklınızdakileri söyleyebilir misiniz?
Steven Shaw

6
Bu muhteşem yazı için teşekkürler. Bazı verilerin program dışında (örneğin bir dosyadan okuma) kaynaklandığı, türlere değerlerin bu tür bir ortamda nasıl tanıtıldığı hakkında bir fikir edinmek için, bağımlı olarak yazılan kodun bazı örneklerini görmek isterim. Tüm örnekler statik olarak bilinen boyutları ile vektörler (liste olarak uygulanır) içerdiği bu duygu var.
tibbe

4
@pigworker Bir efsane olarak "faz ayrımı yok" ifadesini alıyorsunuz (kabul ettiğim diğerleri efsanedir). Ama bunu gördüğüm yazılarda ve konuşmalarda sökmediniz ve bu arada saygı duyduğum bir başka kişi bana "bağımlı tür teorisinin tipik bir derleyiciden farklı olduğunu çünkü tür denetimi, derleme ve yürütme aşamalarını anlamlı bir şekilde ayıramayacağımızı söylüyor. " (bkz. Andrej'in 8 Kasım 2012'nin son gönderisi) Deneyimime göre "sahtecilik" bazen silmeye gerek olmamasına rağmen bazen en azından faz ayrımını bulanıklaştırıyoruz. Burada olmasa da başka bir yerde bu konuda genişleyebilir misiniz?
sclv

4
@sclv Çalışmam özellikle "faz ayrımı yok" efsanesini hedeflemedi, ama diğerleri '. Başlamak için iyi bir yer olarak James McKinna ve Edwin Brady'nin "Epigram Derlemesinde Faz Farklılıkları" başlıklı reddini öneriyorum. Ama aynı zamanda Coq. Daktilo ile yapılan açık dönem değerlendirmesi, ML'ye ekstraksiyon yoluyla uygulamadan tamamen ayrıdır ve ekstraksiyonun türleri ve kanıtları çıkardığı açıktır.
pigworker

20

John, bağımlı türlerle ilgili diğer bir yaygın yanlış anlamadır: yalnızca çalışma zamanında veri mevcut olduğunda çalışmazlar. GetLine örneğini şu şekilde yapabilirsiniz:

data Some :: (k -> *) -> * where
  Like :: p x -> Some p

fromInt :: Int -> Some Natty
fromInt 0 = Like Zy
fromInt n = case fromInt (n - 1) of
  Like n -> Like (Sy n)

withZeroes :: (forall n. Vec n Int -> IO a) -> IO a
withZeroes k = do
  Like n <- fmap (fromInt . read) getLine
  k (vReplicate n 0)

*Main> withZeroes print
5
VCons 0 (VCons 0 (VCons 0 (VCons 0 (VCons 0 VNil))))

Düzenleme: Hm, bu pigworker'ın cevabına bir yorum olması gerekiyordu. Ben açıkça SO başarısız.


İlk cümleniz biraz tuhaf görünüyor; Ben bağımlı tiplerinin nokta onlar olduğunu söyleyebilirim yapmak veriler çalışma seferde sadece kullanılabilir olduğunda işi. Ancak, bu CPS tarzı teknik aynı değildir. Bir işlevin olduğunu varsayalım Vec Zy -> IO String. Bunu kullanamazsınız withZeroes, çünkü tür Zyforall n ile birleştirilemez. Belki bir veya iki özel durum için bunun etrafında çalışabilirsiniz, ancak çabucak kontrolden çıkar.
John L

Basit bir şekilde yazılan bir değeri alırken (getLine'den String gibi) ve onu daha güçlü bir türe (yukarıdaki Natty n gibi) dönüştürürken anahtar denetleyicisini gerekli dinamik denetimleri yaptığınıza ikna etmeniz gerekir. Örneğinizde, rasgele bir sayı okuyorsunuz, bu yüzden forall nmantıklı. Aynı şekilde daha kesin kısıtlamalar uygulanabilir. Bundan daha iyi bir örneğiniz var mı Vec Zy(programın yine de 0 yerine kullanıcı girişini işlemesi gerekir)?
ulfnorell

1
İlk cümle ile söylemek istediğim, zaman zaman dış dünya ile etkileşimde bulunarak verilerinizi alırsanız bağımlı türleri kullanamayacağınıza inanan insanlarla karşılaşıyorum. Demek istediğim, yapmanız gereken tek şey, genellikle basit bir şekilde yazılan, bağımlı olarak yazılan bir ayrıştırıcı yazmaktır.
ulfnorell

1
ulfnorell: Üzgünüm, net değildim. Bir işlevle çalışacak Vec Zy -> IO Stringve diğeri için çalışacağınızı ve Vec n -> IO Stringilkini yalnızca tür eşleştiğinde kullanmak istediğinizi varsayalım . Evet bu mümkün, ancak etkinleştirme mekanizmaları tıknaz. Ve bu çok basit bir mantık; Eğer daha karmaşık bir mantığınız varsa, bu daha kötüdür. Ayrıca, CPS'de çok fazla kod yeniden yazmanız gerekebilir. Ve hala değer düzeyinde bir terime bağlı olan bir tür düzeyinde ifadeniz yok
John L

Ne dediğini anlıyorum. Natty bunun için, n'ye bağlı olarak farklı şeyler yaptığımız vReplicate'de olduğu gibi. Aslında bu biraz tıknaz olabilir. CPS tarzı bir alternatif kadar dolu existentials ile çalışmak için: zeroes :: IO (Some (Flip Vec Int)).
ulfnorell

19

pigworker neden bağımlı türlere yönelmemiz gerektiği konusunda mükemmel bir tartışma sunuyor : (a) harikalar; (b) Haskell'in zaten yaptığı şeyleri basitleştireceklerdi .

"Neden olmasın?" soru, bence birkaç nokta var. İlk nokta, bağımlı türlerin arkasındaki temel kavramın kolay olmasına rağmen (türlerin değerlere bağlı olmasına izin ver), bu temel kavramın sonuçlarının hem ince hem de derin olmasıdır. Örneğin, değerler ve türler arasındaki ayrım hala canlı ve iyidir; ama aralarındaki farkı tartışan olur uzakHindley - Milner veya System F'den daha nüanslı. Bir dereceye kadar bu, bağımlı türlerin temelde zor olmasından kaynaklanmaktadır (örneğin, birinci dereceden mantık kararsızdır). Ama bence en büyük sorun, neler olduğunu yakalamak ve açıklamak için iyi bir kelime dağarcığımız olmaması. Gittikçe daha fazla insan bağımlı türleri öğrendikçe, daha iyi bir kelime dağarcığı geliştireceğiz ve bu nedenle altta yatan problemler hala zor olsa bile işleri daha kolay anlayacağız.

İkinci nokta Haskell'in büyüdüğü gerçeğiyle ilgilidir.bağımlı türlere doğru. Çünkü bu hedefe doğru adım adım ilerliyoruz, ama aslında oraya varmadan, artımlı yamaların üstünde artımlı yamalar olan bir dille sıkıştık. Yeni fikirler popüler hale geldikçe diğer dillerde de aynı şey oldu. Java (parametrik) polimorfizme sahip değildi; ve nihayet eklediklerinde, bazı soyutlama sızıntıları ve sakatlanmış güç ile açık bir şekilde arttı. Görünen o ki, alt tipi ve polimorfizmi karıştırmak doğal olarak zordur; ancak Java Generics'in bu şekilde çalışmasının nedeni bu değildir. Java'nın eski sürümlerinde artan bir gelişme olmasının kısıtlanması nedeniyle çalıştıkları gibi çalışırlar. Ditto, OOP'un icat edildiği ve insanların "objektif" C (Objective-C ile karıştırılmamalıdır), vb. Unutmayın, C ++, C'nin katı bir üst kümesi olma kisvesi altında başladı. Yeni paradigmalar eklemek her zaman dili yeniden tanımlamayı ya da karmaşık bir karmaşa ile sonuçlanmasını gerektirir. Bütün bunlar benim açımdan, Haskell'e gerçek bağımlı türler eklemek, belirli miktarda bağırsak ve yeniden yapılandırma gerektirecek - eğer doğru yaparsak. Ancak bu tür bir elden geçirme taahhüdü vermek zor olsa da, yaptığımız artan ilerleme kısa vadede daha ucuz görünüyor. Gerçekten, GHC'yi hackleyen pek çok insan yok, ancak hayatta kalmak için çok miktarda eski kod var. Bu, DDC, Cayenne, Idris vb.Gibi çok sayıda spinoff dilinin olmasının nedeninin bir parçasıdır. C ++, C'nin katı bir üst kümesi olma kisvesi altında başladı. Yeni paradigmalar eklemek her zaman dili yeniden tanımlamayı ya da karmaşık bir karmaşa ile sonuçlanmasını gerektirir. Bütün bunlar benim açımdan, Haskell'e gerçek bağımlı türler eklemek, belirli miktarda bağırsak ve yeniden yapılandırma gerektirecek - eğer doğru yaparsak. Ancak bu tür bir elden geçirme taahhüdü vermek zor olsa da, yaptığımız artan ilerleme kısa vadede daha ucuz görünüyor. Gerçekten, GHC'yi hackleyen pek çok insan yok, ancak hayatta kalmak için çok miktarda eski kod var. Bu, DDC, Cayenne, Idris vb.Gibi çok sayıda spinoff dilinin olmasının nedeninin bir parçasıdır. C ++, C'nin katı bir üst kümesi olma kisvesi altında başladı. Yeni paradigmalar eklemek her zaman dili yeniden tanımlamayı ya da karmaşık bir karmaşa ile sonuçlanmasını gerektirir. Bütün bunlar benim açımdan, Haskell'e gerçek bağımlı türler eklemek, belirli miktarda bağırsak ve yeniden yapılandırma gerektirecek - eğer doğru yaparsak. Ancak bu tür bir elden geçirme taahhüdü vermek zor olsa da, yaptığımız artan ilerleme kısa vadede daha ucuz görünüyor. Gerçekten, GHC'yi hackleyen pek çok insan yok, ancak hayatta kalmak için çok miktarda eski kod var. Bu, DDC, Cayenne, Idris vb.Gibi çok sayıda spinoff dilinin olmasının nedeninin bir parçasıdır. veya karmaşık bir karmaşa ile sonuçlanır. Bütün bunlar benim açımdan, Haskell'e gerçek bağımlı türler eklemek, belirli miktarda bağırsak ve yeniden yapılandırma gerektirecek - eğer doğru yaparsak. Ancak bu tür bir elden geçirme taahhüdü vermek zor olsa da, yaptığımız artan ilerleme kısa vadede daha ucuz görünüyor. Gerçekten, GHC'yi hackleyen pek çok insan yok, ancak hayatta kalmak için çok miktarda eski kod var. Bu, DDC, Cayenne, Idris vb.Gibi çok sayıda spinoff dilinin olmasının nedeninin bir parçasıdır. veya karmaşık bir karmaşa ile sonuçlanır. Bütün bunlar benim açımdan, Haskell'e gerçek bağımlı türler eklemek, belirli miktarda bağırsak ve yeniden yapılandırma gerektirecek - eğer doğru yaparsak. Ancak bu tür bir elden geçirme taahhüdü vermek zor olsa da, yaptığımız artan ilerleme kısa vadede daha ucuz görünüyor. Gerçekten, GHC'yi hackleyen pek çok insan yok, ancak hayatta kalmak için çok miktarda eski kod var. Bu, DDC, Cayenne, Idris vb.Gibi çok sayıda spinoff dilinin olmasının nedeninin bir parçasıdır. bu tür bir elden geçirme taahhüdü vermekte zorlanırken, kaydettiğimiz artan ilerleme kısa vadede daha ucuz görünüyor. Gerçekten, GHC'yi hackleyen pek çok insan yok, ancak hayatta kalmak için çok miktarda eski kod var. Bu, DDC, Cayenne, Idris vb.Gibi çok sayıda spinoff dilinin olmasının nedeninin bir parçasıdır. bu tür bir elden geçirme taahhüdü vermekte zorlanırken, kaydettiğimiz artan ilerleme kısa vadede daha ucuz görünüyor. Gerçekten, GHC'yi hackleyen pek çok insan yok, ancak hayatta kalmak için çok miktarda eski kod var. Bu, DDC, Cayenne, Idris vb.Gibi çok sayıda spinoff dilinin olmasının nedeninin bir parçasıdır.

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.