Bağımlı yazım nedir?


84

Birisi bana bağımlı yazmayı açıklayabilir mi? Haskell, Cayenne, Epigram veya diğer işlevsel dillerde çok az deneyimim var, bu yüzden ne kadar basit terimler kullanırsanız, onu o kadar çok takdir edeceğim!


Peki, örneğin wikipedia makalesi hakkında tam olarak neyi anlamadınız?
Karl Knechtel

127
Makale, bana bir tür koyun eti gibi gelen lambda küpleriyle açılıyor. Sonra λΠ2 sistemlerini tartışmaya devam ediyor ve yabancı konuşmadığım için bu bölümü atladım. Daha sonra, tesadüfen kalkülüs, ısı transferi veya inşaat ile pek ilgisi yok gibi görünen endüktif yapıların hesabını okudum. Bir dil karşılaştırma tablosu verdikten sonra makale bitiyor ve sayfaya geldiğimden daha fazla kafam karışıyor.
Nick

3
@Nick Wikipedia ile ilgili genel bir sorun bu. Yorumunuzu birkaç yıl önce gördüm ve o zamandan beri hatırlıyorum. Şimdi işaretliyorum.
Daniel H

Yanıtlar:


116

Şunu bir düşünün: tüm uygun programlama dillerinde işlevler yazabilirsiniz, örn.

def f(arg) = result

Burada fbir değer alır argve bir değer hesaplar result. Değerlerden değerlere bir fonksiyondur.

Şimdi, bazı diller polimorfik (diğer adıyla genel) değerleri tanımlamanıza izin verir:

def empty<T> = new List<T>()

Burada emptybir tür alır Tve bir değer hesaplar. Türlerden değerlere bir işlevdir.

Genellikle, genel tür tanımlarına da sahip olabilirsiniz:

type Matrix<T> = List<List<T>>

Bu tanım bir tür alır ve bir tür döndürür. Türlerden türlere bir işlev olarak görülebilir.

Sıradan dillerin sunduğu şey için çok fazla. Dördüncü olasılık, yani işlevleri değerlerden türlere tanımlama olanağı sunuyorsa, bir dil bağımlı olarak yazılır. Veya başka bir deyişle, bir tür tanımını bir değer üzerinden parametreleştirmek:

type BoundedInt(n) = {i:Int | i<=n}

Bazı ana dillerde bunun karıştırılmaması gereken bazı sahte biçimleri vardır. Örneğin, C ++ 'da, şablonlar değerleri parametre olarak alabilir, ancak uygulandıklarında derleme zamanı sabitleri olmaları gerekir. Gerçekten bağımlı olarak yazılmış bir dilde öyle değil. Örneğin, yukarıdaki türü şu şekilde kullanabilirim:

def min(i : Int, j : Int) : BoundedInt(j) =
  if i < j then i else j

Burada, işlevin sonucu türü bağlıdır gerçek argüman değerine j, dolayısıyla terminoloji.


BoundedIntYine de örnek bir Ayrıntılandırma Türü değil mi? Bu 'oldukça yakın', ancak tam olarak 'bağımlı türler' değil, örneğin İdris'in dep.typing hakkında bir eğitimde ilk olarak bahsettiği türden.
Narfanar

3
@Noein, iyileştirme türleri aslında bağımlı türlerin basit bir biçimidir.
Andreas Rossberg

22

Bağımlı türler , derleme sırasında daha büyük mantık hataları kümesinin ortadan kaldırılmasını sağlar . Bunu açıklamak için aşağıdaki fonksiyon özelliklerini dikkate alın :f

İşlev , girdi olarak fyalnızca çift tam sayıları almalıdır .

Bağımlı türler olmadan şuna benzer bir şey yapabilirsiniz:

def f(n: Integer) := {
  if  n mod 2 != 0 then 
    throw RuntimeException
  else
    // do something with n
}

Burada derleyici ngerçekten çift olup olmadığını algılayamaz , yani derleyicinin bakış açısından aşağıdaki ifade uygundur:

f(1)    // compiles OK despite being a logic error!

Bu program çalışır ve ardından çalışma zamanında istisna atar, yani programınızda bir mantık hatası vardır.

Şimdi, bağımlı türler çok daha anlamlı olmanızı sağlar ve şuna benzer bir şey yazmanıza olanak tanır:

def f(n: {n: Integer | n mod 2 == 0}) := {
  // do something with n
}

İşte nbağımlı tipte {n: Integer | n mod 2 == 0}. Bunu yüksek sesle okumak yardımcı olabilir

n her bir tamsayı 2'ye bölünebilecek şekilde bir tamsayılar kümesinin üyesidir.

Bu durumda derleyici, derleme zamanında tek bir sayı ilettiğiniz bir mantık hatasını algılar fve programın ilk etapta çalıştırılmasını engeller:

f(1)    // compiler error

İşte Scala yoluna bağlı türleri kullanan açıklayıcı bir örnek , fböyle bir gereksinimi karşılayan işlevi nasıl uygulamaya çalışabileceğimize dair :

case class Integer(v: Int) {
  object IsEven { require(v % 2 == 0) }
  object IsOdd { require(v % 2 != 0) }
}

def f(n: Integer)(implicit proof: n.IsEven.type) =  { 
  // do something with n safe in the knowledge it is even
}

val `42` = Integer(42)
implicit val proof42IsEven = `42`.IsEven

val `1` = Integer(1)
implicit val proof1IsOdd = `1`.IsOdd

f(`42`) // OK
f(`1`)  // compile-time error

Anahtar, değerin değer ntüründe nasıl göründüğüne dikkat etmektir, proofyani n.IsEven.type:

def f(n: Integer)(implicit proof: n.IsEven.type)
      ^                           ^
      |                           |
    value                       value

Biz demek tipi n.IsEven.type bağlıdır değeri n dolayısıyla dönem bağımlı-tipleri .


6
Rastgele değerle nasıl başa çıkıyor? Örneğin, f(random())derleme hatasıyla sonuçlanır mı?
Wong Jia Hau

6
fBir ifadeye başvurmak , derleyicinin (sizin yardımınızla veya yardımınız olmadan) ifadenin her zaman eşit olmasını ve böyle bir kanıtın bulunmadığını random()(çünkü aslında garip olabileceğinden) f(random())sağlamasını gerektirir , bu nedenle derleme başarısız olur.
Matthijs

19

C ++ 'yı biliyorsanız, motive edici bir örnek vermek kolaydır:

Diyelim ki bir kapsayıcı türü ve bunun iki örneği var

typedef std::map<int,int> IIMap;
IIMap foo;
IIMap bar;

ve şu kod parçasını göz önünde bulundurun (foo'nun boş olmadığını varsayabilirsiniz):

IIMap::iterator i = foo.begin();
bar.erase(i);

Bu apaçık bir çöplüktür (ve muhtemelen veri yapılarını bozar), ancak "iteratörden foo'ya" ve "çubuğa yineleyici" aynı türden olduğu için, IIMap::iteratoranlamsal olarak tamamen uyumsuz olsalar bile , yazım denetimi iyi olacaktır .

Sorun, bir yineleyici türünün yalnızca kap türüne değil, aslında kap nesnesine bağlı olması gerektiğidir, yani "statik olmayan üye türü" olmalıdır:

foo.iterator i = foo.begin();
bar.erase(i);  // ERROR: bar.iterator argument expected

Böyle bir özellik, bir terime (foo) bağlı olan bir türü (foo.iterator) ifade etme yeteneği, tam olarak bağımlı yazmanın ne anlama geldiğidir.

Bu özelliği sık sık görmemenizin nedeni, büyük bir solucan kutusu açmasıdır: Birdenbire, iki türün aynı olup olmadığını kontrol etmek için derleme sırasında iki ifadeyi kanıtlamak zorunda kalırsınız. eşdeğerdir (çalışma zamanında her zaman aynı değeri verir). Eğer Wikipedia'nın karşılaştırırsanız sonucunda, bağımlı yazılan diller listesini onun ile teorem ispatlayıcılar listesinde Şüpheli benzerlik fark edebilirsiniz. ;-)


4

Kitap Türleri ve Programlama Dillerinden Alıntı Yapma (30.5):

Bu kitabın çoğu, çeşitli türlerdeki soyutlama mekanizmalarının resmileştirilmesiyle ilgiliydi. Basit tipte lambda-hesaplamasında, bir terimi alma ve bir alt terimi soyutlama işlemini resmileştirdik, daha sonra farklı terimlere uygulayarak somutlaştırılabilecek bir fonksiyon elde ettik. Sistemde F, bir terimi alma ve bir türü soyutlama, onu çeşitli türlere uygulayarak somutlaştırılabilen bir terim ortaya çıkarma işlemini düşündük. İçindeλω, daha sonra farklı türlere uygulayarak somutlaştırılabilecek bir tip operatörü elde etmek için bir tür alıp bir alt ifadeyi soyutlayarak basit tipte lambda-hesap "bir seviye yukarı" mekanizmasını özetledik. Tüm bu soyutlama biçimlerini düşünmenin uygun bir yolu, diğer ifadelerle indekslenen ifade aileleri açısından düşünmektir. Sıradan bir lambda soyutlaması λx:T1.t2, terimlere göre [x -> s]t1indekslenen bir terimler ailesidir s. Benzer şekilde, bir tür soyutlaması λX::K1.t2, türlere göre indekslenen bir terimler ailesidir ve bir tür operatörü, türlere göre indekslenen bir türler ailesidir.

  • λx:T1.t2 terimlere göre indekslenen terimler ailesi

  • λX::K1.t2 türlere göre indekslenmiş terimler ailesi

  • λX::K1.T2 türlere göre indekslenen türler ailesi

Bu listeye bakıldığında, henüz dikkate almadığımız bir olasılık olduğu açıktır: terimlere göre indekslenmiş tür aileleri. Bu tür soyutlama, bağımlı türler başlığı altında da kapsamlı bir şekilde incelenmiştir.

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.