Tam olarak Haskell tip sistemini bu kadar çok saygı gören (vs, Java gibi) yapan nedir?


204

Haskell'i öğrenmeye başladım . Ben çok yeni biriyim ve kafamı temel yapıları etrafında gezdirmek için birkaç çevrimiçi kitabı okuyorum.

Tanıdığı insanların sıkça konuştukları 'mem'lerden biri, "derlerse işe yarar *" meselesidir - bence bu, tip sisteminin gücüyle ilgili olduğunu düşünüyorum.

Neden tam olarak Haskell'in bu konuda diğer statik olarak yazılmış dillerden daha iyi olduğunu anlamaya çalışıyorum .

Başka bir deyişle, Java'da, ArrayList<String>()gerçekten olması gereken bir şeyi içermek için gömme gibi bir şey yapabileceğini farz ediyorum ArrayList<Animal>(). Burada iğrenç şey senin olmasıdır stringiçeren elephant, giraffevb, ve birisi de koyarsa Mercedes- derleyicinizin size yardımcı olmayacaktır.

Ben ise yaptığı yapmak ArrayList<Animal>()benim program araçların hakkında, hayvanlar ilgili değil karar verirseniz, zamanla daha ileri noktada, o zaman, o zaman, diyelim ki, üreten bir işlev değiştirebilir ArrayList<Animal>üretmek ArrayList<Vehicle>ve benim IDE orada beni her yerde söylemelidir bir derleme sonu.

Benim varsayım, insanların güçlü bir tip sistemle kastettiği şeydir , ancak Haskell'in neden daha iyi olduğu bana açık değil. Başka bir deyişle, iyi veya kötü Java yazabilirsiniz, Haskell'de de aynı şeyi yapabileceğinizi varsayıyorum (yani, gerçekten birinci sınıf veri türleri olması gereken dizgilere / ints'e şeyler ekler).

Temel / önemli bir şeyi kaçırdığımdan şüpheleniyorum.
Yollarımdaki hatayı göstermekten çok mutlu olurum!


31
İnsanların gerçek cevapları yazdıklarımdan daha bilgili olmalarına izin vereceğim, ancak bunun özü şudur: C # gibi statik olarak yazılmış diller, savunulabilir kod yazmanıza yardımcı olacak bir tür sisteme sahiptir ; Haskell gibi sistemleri yazın ( doğrulanabilir iseniz ). İş yerindeki temel ilke, derleme aşamasına kontrol edilebilecek şeyleri taşımak; Haskell derleme zamanında daha fazla şeyi kontrol eder .
Robert Harvey,

8
Haskell hakkında fazla bir şey bilmiyorum ama Java hakkında konuşabiliyorum. O iken görünür kesinlikle yazılan, hala dediğin gibi "iğrenç" şeyler yapmak için izin verir. Java'nın tip sistemiyle ilgili yaptığı hemen hemen her garanti için, bunun bir yolu var.

12
Neden tüm cevapların Maybesadece sona doğru geldiğini bilmiyorum . Haskell'den daha popüler dillerin ödünç alacağı bir şey seçmek zorunda kalsaydım, bu olurdu. Bu çok basit bir fikir (teorik açıdan çok ilginç değil), ancak bu tek başına işlerimizi çok daha kolaylaştıracak.
Paul,

1
Burada büyük cevaplar olacak, ancak yardımcı olma çabasıyla, imza türlerini inceleyin. Onlar insanlar ve programlar için izin ikna türleri: Java Duygusal orta re nasıl gösterilecektir şekilde programları hakkında.
Michael Easter

6
Adalet için, "derlerse işler işe yarayacak bir bütün" ifadesinin gerçek bir ifade değil sloganı olduğunu belirtmeliyim. Evet, biz Haskell programcıları tip denetleyicisini geçmenin bazı sınırlı doğruluk nosyonları için iyi bir doğruluk şansı sağladığını biliyoruz, ancak bu kesinlikle gerçek anlamda ve evrensel olarak "doğru" bir ifade değil!
Tom Ellis,

Yanıtlar:


230

İşte Haskell'de bulunan ve Java'da kullanılamayan ya da daha az iyi olan sıralanmamış bir tür sistem özellikleri listesi (bildiğim kadarıyla, Java ile zayıf olduğu için)

  • Güvenlik . Haskell'in tipleri oldukça iyi bir "tip güvenliği" özelliğine sahiptir. Bu oldukça spesifiktir, ancak esasen, bir türdeki değerlerin istemeden başka bir türe dönüşemediği anlamına gelir. Bu bazen değişken olma ihtimali yüksektir (bkz. OCaml’ın değer kısıtlaması ).
  • Cebirsel Veri Tipleri . Haskell'deki türler temel olarak lise matematiği ile aynı yapıya sahiptir. Bu, son derece basit ve tutarlıdır, ancak göründüğü kadarıyla, istediğiniz kadar güçlü olabilir. Tipik bir sistem için harika bir temel.
    • Veri türü-jenerik programlama . Bu genel tiplerle aynı değildir ( genelleştirmeye bakınız ). Bunun yerine, daha önce belirtildiği gibi tip yapının basitliği nedeniyle, bu yapı üzerinde genel olarak çalışan kod yazması nispeten kolaydır. Daha sonra Equality gibi bir şeyin Haskell derleyici tarafından kullanıcı tanımlı bir tür için nasıl otomatik olarak türetileceğinden bahsederim . Temel olarak bunu yapma şekli, herhangi bir kullanıcı tanımlı türün altında yatan ortak, basit yapının üzerinde yürümek ve değerler arasında eşleşmektir - çok doğal bir yapısal eşitlik biçimi.
  • Karşılıklı özyinelemeli türleri . Bu, önemsiz olmayan türler yazmanın sadece önemli bir bileşenidir.
    • İç içe türleri . Bu, farklı türlerde yinelenen değişkenler üzerinden özyinelemeli türleri tanımlamanıza izin verir. Örneğin, bir tür dengeli ağaç vardır data Bt a = Here a | There (Bt (a, a)). Geçerli değerleri dikkatlice düşünün Bt ave bu tipin nasıl çalıştığını not edin. Bu zor!
  • Genelleme . Bu tür bir sistemde bulunmamak için neredeyse aptalca (ahem, sana bakmak, Go). Tür değişkenleri kavramlarına ve bu değişkenin seçiminden bağımsız olan kod hakkında konuşma yeteneğine sahip olmak önemlidir. Hindley Milner Sistem F Haskell'ın tür sistemi türetilmiş bir tipi sistem HM tiplemesi bir detaylandırılmasıydı ve Sistem F esasen ocak genelleme. Söylemek istediğim, Haskell'in çok iyi bir genelleme hikayesi olduğu.
  • Özet türleri . Haskell'in buradaki hikayesi harika değil, aynı zamanda da mevcut değil. Genel bir arayüze sahip ancak özel bir uygulamaya sahip türleri yazmak mümkündür. Bu, daha sonra uygulama kodunda yapılan değişiklikleri kabul etmemize izin verir ve daha da önemlisi Haskell'deki tüm işlemlerin temeli olduğu için, iyi tanımlanmış arayüzleri olan "büyü" türlerini yazabilir IO. Java muhtemelen dürüst olmak gerekirse, daha hoş bir soyut tür hikayesi vardır, ancak Arayüzlerin daha popüler hale gelene kadar gerçekten doğru olduğunu sanmıyorum.
  • Parametricity . Haskell değerleri yok herhangi evrensel işlemleri. Java bunu referans eşitliği ve karmaşası gibi şeylerle ve daha da zorlayıcı bir şekilde zorlamalarla ihlal ediyor. Bunun anlamı, bir işlemin veya değerin anlamını tamamen türünden dikkate değer bir dereceye kadar bilmenizi sağlayan türler hakkında ücretsiz teoremler edinmenizdir - bazı türler, yalnızca çok az sayıda kişinin olabileceği şekildedir.
  • Daha yüksek kalitedeki türler , daha zor olan şeyleri kodlarken tüm türü gösterir. Functor / Uygulamalı / Monad, Katlanabilir / Traversable, tüm mtlefekt yazma sistemi, genelleştirilmiş functor fixpoints. Liste uzayıp gidiyor. Daha yüksek türlerde en iyi ifade edilen ve göreceli olarak az sayıda tip sistemde kullanıcının bu konular hakkında konuşmasına izin veren birçok şey vardır.
  • Sınıfları yazın . Eğer sistemleri mantık olarak düşünürseniz - ki bu yararlıdır - o zaman genellikle bir şeyler kanıtlamanız istenir. Çoğu durumda bu esas olarak hat gürültüsüdür: sadece bir doğru cevap olabilir ve programcının bunu belirtmesi zaman ve çaba kaybıdır. Tipklasss, Haskell'in sizin için ispatları üretmesinin bir yoludur. Daha somut bir ifadeyle, bu basit "tip denklem sistemlerini" çözmenizi sağlar, "Hangi tür (+)şeyleri birlikte yapmayı amaçlıyoruz ? Oh Integer, tamam! Şimdi doğru kodu girelim!". Daha karmaşık sistemlerde daha ilginç kısıtlamalar oluşturuyor olabilirsiniz.
    • Kısıtlama hesabı . Tipik sınıf prolog sistemine ulaşma mekanizması olan Haskell'deki kısıtlamalar yapısal olarak yazılmıştır. Bu , daha basit olanlardan karmaşık sınırlamalar oluşturmanıza olanak sağlayan çok basit bir alt kalıplama ilişkisi biçimi verir. mtlKütüphanenin tamamı bu fikir üzerine kuruludur.
    • Türetmek . Tipik sınıf sisteminin kanonikliğini sürdürebilmek için kullanıcı tanımlı türlerin başlatması gereken kısıtlamaları tanımlamak için çoğu zaman önemsiz kod yazmak gerekir. Haskell tiplerinin normal yapısına göre, derleyiciden sizin için bu kazanı yapmasını istemek genellikle mümkündür.
    • Sınıf prolog yazın . Haskell tipi sınıf çözücü - daha önce bahsettiğim bu "kanıtları" oluşturan sistem - aslında daha iyi anlamsal özelliklere sahip, sakat bir Prolog şeklidir. Bu, prolog tipinde gerçekten kıllı şeyleri kodlayabileceğiniz ve bunların derleme zamanında ele alınmasını bekleyebileceğiniz anlamına gelir. İyi bir örnek, siparişi unutursanız iki heterojen listenin eşdeğer olduğuna dair bir kanıt bulmak olabilir - bunlar eşdeğer heterojen "kümelerdir".
    • Çok parametreli tip sınıfları ve fonksiyonel bağımlılıklar . Bunlar, sadece sınıf sınıfı prologunun temelini oluşturmak için büyük ölçüde yararlı iyileştirmelerdir. Prolog'u biliyorsanız, birden fazla değişkenin tahminlerini yazarken ifade gücünün ne kadar arttığını tahmin edebilirsiniz.
  • Oldukça iyi çıkarım . Hindley Milner tipi sistemlere dayanan diller oldukça iyi çıkarımlara sahiptir. HM'nin kendisi tam bir çıkarıma sahiptir, yani hiçbir zaman bir tür değişkeni yazmanıza gerek kalmaz. Haskell'in en basit şekli olan Haskell 98, bunu çok nadir durumlarda ortaya çıkarmaktadır. Genel olarak, modern Haskell, HM'ye daha fazla güç ekleyerek ve kullanıcıların ne zaman şikayet ettiğini görmek için tam çıkarım alanını yavaşça azaltmada bir deney olmuştur. İnsanlar çok nadir şikayet ederler- Haskell'in çıkarımı oldukça iyidir.
  • Yalnızca çok, çok, çok zayıf bir alt tipleme . Daha önce de belirttiğim gibi, typeclass prolog kısıtlama sistemi yapısal alt tipleme kavramına sahipti. Haskell'deki tek alt türleme şekli budur . Altyazı, muhakeme ve çıkarım için korkunç. Bu problemlerin her birini önemli ölçüde zorlaştırır (bir eşitlik sistemi yerine eşitsizlik sistemi). Ayrıca yanlış anlaşılması gerçekten de çok kolay (Alt sınıflama altyazı ile aynı mı? Tabii ki değil! Ama insanlar çok kafa karıştırıyor ve birçok dil bu karışıklığa yardım ediyor! Buraya nasıl geldik?
    • Not geçenlerde (erken 2017) Steven Dolan yayınlanan tez üzerinde MLsub , bir sahiptir ML ve Hindley'nin-Milner tür kesmesi bir varyantı çok güzel subtiplemesi hikaye ( ayrıca bkz ). Bu, yukarıda yazdıklarımı engellemiyor - çoğu alt sistem kırılmış ve kötü çıkarım - ancak bugün tam bir çıkarım ve alt yazı oyununu güzelce yapmanın umut verici bazı yollarını keşfettiğimizi gösteriyor. Şimdi, açıkça anlaşılacağı gibi, Java'nın alt yazma kavramları, Dolan'ın algoritmalarından ve sistemlerinden hiçbir şekilde yararlanamaz. Alt yazmanın ne anlama geldiğini yeniden düşünmeyi gerektirir.
  • Daha yüksek rütbe türleri . Daha önce genelleme hakkında konuştum, fakat sadece genelleme yapmaktan daha fazlası, içinde genelleştirilmiş değişkenleri olan türler hakkında konuşabilmek faydalıdır . Örneğin, bu yapıların "içerdikleri" türüne neyin habersiz olduğu ( parametrikliğe bakınız ) yüksek dereceli yapılar arasındaki bir eşleme (forall a. f a -> g a). Düz HM size bu tür bir işlev yazabilir, ama yüksek rütbe türleriyle bir gibi bir işlevi talep argüman şöyle: mapFree :: (forall a . f a -> g a) -> Free f -> Free g. aDeğişkenin yalnızca argüman içinde bağlı olduğuna dikkat edin . Bu , işlevin tanımlayıcısının , kullanıcıyı değil, kullandıklarında mapFreeneyin abaşlatıldığına karar vereceği anlamına gelir mapFree.
  • Varolan türler . Yüksek dereceli türler evrensel niceliklendirme hakkında konuşmamıza izin verirken, varoluşsal türler varoluşsal niceliklendirme hakkında konuşmamıza izin verir: sadece bazı denklemleri karşılayan bilinmeyen tiplerin olduğu fikri . Bu yararlı olur ve daha uzun süre devam etmesi uzun sürecektir.
  • Aileleri yazın . Tipik sınıf mekanizmaları bazen Prolog'da her zaman düşünmediğimiz için uygun değildir. Yazı aileleri, türler arasında düz fonksiyonel ilişkiler yazmamızı sağlar .
    • Kapalı tip aileler . Yazı tipi aileleri varsayılan olarak açıktır; bu durum can sıkıcıdır, çünkü onları dilediğiniz zaman uzatabilirseniz, onları herhangi bir başarı umuduyla "tersine çeviremezsiniz" anlamına gelir. Bunun nedeni , enjekte edilemezliği kanıtlayamayacağınız , ancak kapalı tip ailelerle yapabileceğinizdir.
  • Tür endeksli türleri ve tür tanıtımı . Bu noktada gerçekten egzotik oluyorum, ancak bunların zaman zaman pratik kullanımı var. Açık veya kapalı tipte bir tutamaç yazmak isterseniz, bunu çok güzel yapabilirsiniz. Aşağıdaki pasajda State, değerlerinin de tip seviyesine yükseltildiği çok basit bir cebirsel tip olduğuna dikkat edin . Ardından, daha sonra hakkında konuşmak tip yapıcıları gibi Handlespesifik olarak argümanlar alarak çeşitleri gibi State. Tüm detayları anlamak kafa karıştırıcı, ama aynı zamanda çok doğru.

    data State = Open | Closed
    
    data Handle :: State -> * -> * where
      OpenHandle :: {- something -} -> Handle Open a
      ClosedHandle :: {- something -} -> Handle Closed a
    
  • Çalışan çalışma zamanı türü gösterimleri . Java, tür silme ve bazı insanların geçitlerinde yağmur yağma özelliğine sahip olmasıyla ünlüdür. Tür silme , gitmek için doğru yoldur, ancak, bir işleve getRepr :: a -> TypeReprsahipmişsiniz gibi parametrikliği en aza indirmiş olursunuz. Daha da kötüsü, çalışma zamanında güvensiz baskıları tetiklemek için kullanılan, kullanıcı tarafından oluşturulmuş bir işlevse, o zaman büyük bir güvenlik sorununa sahip olmanızdır . Haskell'in Typeablesistemi kasa oluşturulmasına izin veriyor coerce :: (Typeable a, Typeable b) => a -> Maybe b. Bu sistem Typeablederleyicide (kullanıcılara değil) uygulanmaya dayanır ve Haskell'in sınıf sınıfı mekanizması ve uyması gereken yasalar olmadan bu kadar güzel bir anlam ifade edilemezdi.

Ancak bunlardan çok, Haskell'in tür sisteminin değeri, türlerin dili nasıl tanımladığı ile de ilgilidir. İşte Haskell'in yazı sistemi üzerinden değer katan birkaç özelliği.

  • Saflık . Haskell, "yan etkinin" çok, çok, çok geniş bir tanımı için hiçbir yan etkiye izin vermez. Bu, türleri girdiler ve çıktıları yöneten ve yan etkiler olmayan her şeyin girdiler ve çıktılarda hesaba katılması gerektiğinden , daha fazla bilgi vermenizi sağlar .
    • IO . Daha sonra, Haskell'in yan etkiler hakkında konuşmanın bir yoluna ihtiyacı vardı - çünkü herhangi bir gerçek program bazılarını içermelidir - bu nedenle, bir sınıf, yüksek tür ve soyut türlerin bir kombinasyonu, IO atemsil etmek için çağrılan özel, süper özel bir tür kullanılması fikrine yol açtı. tür değerleri ile sonuçlanan yan etkili hesaplamalar a. Bu, saf bir dilin içine yerleştirilmiş çok hoş bir efekt sisteminin temelidir .
  • Eksikliğinull . Herkes bunun nullmodern programlama dillerinin milyar dolarlık hata olduğunu biliyor . Cebirsel tipleri, sadece ekleme yeteneği özellikle bir tür dönüştürerek sahip türleri üzerine devlet "yok" Atürü içine Maybe Atamamen sorununu hafifletmek, null.
  • Polimorfik özyineleme . Bu, tür değişkenlerini her özyinelemeli çağrıdaki farklı türlerde kullanmasına rağmen kendi genellemelerinde tanımlayabilmenizi sağlar. Bunun hakkında konuşmak zor, ama özellikle yuvalanmış türler hakkında konuşmak için kullanışlıdır. Geri Bakın Bt aönce gelen tipi ve boyutunu hesaplamak için bir işlev yazmaya çalışıyorum: size :: Bt a -> Int. Biraz benzeyecek size (Here a) = 1ve size (There bt) = 2 * size bt. Operasyonel olarak bu çok karmaşık değildir, ancak sizeson denklemdeki özyinelemeli çağrının farklı bir türde yapıldığına dikkat edin, ancak genel tanımın genelleştirilmiş hoş bir türü vardır size :: Bt a -> Int. Bunun toplam çıkarımı kıran bir özellik olduğunu unutmayın, ancak bir tür imza verirseniz Haskell buna izin verir.

Devam edebilirdim, ama bu liste seni biraz daha baştan başlatmalıydı.


7
Boş bir milyar dolarlık "hata" değildi. Herhangi bir anlamlı olması muhtemel olmadan işaretçinin reddedilmeyeceğini statik olarak doğrulamanın mümkün olmadığı durumlar vardır ; Böyle bir durumda denenmiş bir tuzak tuzağına sahip olmak, genellikle işaretçinin başlangıçta anlamsız bir nesneyi tanımlamasını gerektirmekten daha iyidir. Ben en büyük boş ilgili hata, hangi verilen uygulamaları görüyordum üzerinde, yakalanacak ama üzerinde tuzak olmaz ne dechar *p = NULL;*p=1234char *q = p+5678;*q = 1234;
SuperCat

37
Bu, sadece Tony Hoare'den alıntı: en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions . nullİşaretçi aritmetikte gerekli olan zamanlar olduğundan emin olduğum halde , bunun yerine işaretçi aritmetiğinin, dilinizin anlamını barındırmak için yanlış bir yer olmadığını, bunun yanlış olduğunu söylemediğini yorumluyorum.
J. Abrahamson,

18
@supercat, gerçekten null olmadan bir dil yazabilirsiniz. Buna izin verip vermemek bir seçimdir.
Paul Draper

6
@supercat - Bu problem Haskell'de de var ama farklı bir biçimde. Haskell normalde tembel ve değişkendir ve bu nedenle değerlendirilmediği p = undefinedsürece yazmanıza izin verir p. Daha faydalı bir şekilde, undefinedyeniden değerlendirilmediğiniz sürece bir çeşit değişken referansı kullanabilirsiniz. Daha ciddi olan, elbette ki kararsız olan, sonlandırılamayan tembel hesaplamalardır. Asıl fark, bunların hepsinin açıkça programlama hataları olması ve asla sıradan bir mantığı ifade etmek için kullanılmamasıdır.
Christian Conkle

6
@supercat Haskell tamamen referans semantiğinden yoksundur (bu, referansların referansları ile değiştirilerek her şeyin korunmasını gerektiren referans şeffaflığı kavramıdır ). Bu nedenle, sorunuzun açık olmadığını düşünüyorum.
J. Abrahamson

78
  • Tam tip çıkarım. Aslında edebilirsiniz kullanmak "Şimdiye kadar yaptığım yazma tipi imzaları, Mescid bok." Hazır ve nazır gibi hissetmeden karmaşık türleri
  • Tipler tamamen cebirseldir , bu da bazı karmaşık fikirleri ifade etmeyi çok kolaylaştırır.
  • Haskell, bir tür için tüm uygulamaları aynı yere koymanız gerekmediği sürece, benzer arayüzler olan tip sınıflarına sahiptir. Kaynaklarına erişmelerine gerek kalmadan mevcut üçüncü taraf türleri için kendi sınıf sınıflarınızın uygulamalarını oluşturabilirsiniz.
  • Yüksek dereceli ve özyinelemeli işlevler, tür denetleyicisinin görünümüne daha fazla işlevsellik koyma eğilimindedir. Örneğin, filtre al . Zorunlu bir dilde, foraynı işlevi gerçekleştirmek için bir döngü yazabilirsiniz , ancak bir fordöngüde geri dönüş türü kavramı olmadığı için aynı statik tip garantisine sahip olmazsınız .
  • Alt tiplerin olmaması parametrik polimorfizmi büyük ölçüde basitleştirir.
  • Daha yüksek türler (tür türleri), Haskell'de belirtilmesi ve kullanılması nispeten kolaydır, bu da Java'da tamamen anlaşılamayan türler etrafında soyutlamalar oluşturmanıza olanak tanır.

7
Güzel cevap - bana daha kibar bir türden basit bir örnek verebilir misiniz, bunun java'da neden yapmanın imkansız olduğunu anlamama yardımcı olacağını düşünün.
phatmanace

3
Burada bazı güzel örnekler var .
Karl Bielefeldt

3
Desen eşleştirme de gerçekten önemlidir, bu, kararları kolayca süper yapmak için bir nesnenin türünü kullanabileceğiniz anlamına gelir.
Benjamin Gruenbaum

2
@ BenjaminGruenbaum Buna bir tür sistem özelliği diyebileceğimi sanmıyorum.
Doval

3
ADT'ler ve HKT'ler kesinlikle cevabın bir parçası olsalar da, bu soruyu soran kimsenin neden faydalı olduklarını bileceğinden şüpheliyim, bunu açıklamak için her iki bölümün de genişletilmesi gerektiğini düşünüyorum
jk.

62
a :: Integer
b :: Maybe Integer
c :: IO Integer
d :: Either String Integer

Haskell'de: bir tamsayı, boş olabilecek bir tamsayı, değeri dış dünyadan gelen bir tamsayı ve bunun yerine bir dize olabilecek bir tamsayı tamamen farklı türlerdir - ve derleyici bunu uygulayacaktır . Bu ayrımlara saygı göstermeyen bir Haskell programını derleyemezsiniz.

(Bununla birlikte, tür bildirimlerini çıkartabilirsiniz. Çoğu durumda, derleyici, başarılı bir derlemeyle sonuçlanacak değişkenleriniz için en genel türü belirleyebilir. Bu düzgün değil mi?)


11
+1 bu cevap tamamlanmadıysa, soru düzeyinde çok daha iyi olduğunu düşünüyorum
jk.

1
+1 Diğer dillerin de Maybe(örneğin, Java Optionalve Scala'nın Option) olduğunu açıklamaya yardımcı olsa da, bu dillerde yarı-pişmiş bir çözümdür, çünkü her zaman nullbu tip bir değişkene atayabilir ve programınızın çalışması sırasında patlayabilir. saati. Bu Haskell [1] ile olamaz, çünkü boş değer yoktur , bu yüzden basitçe hile yapamazsınız. ([1]: Aslında, bu tür kısmi fonksiyonlar kullanılarak NullPointerException benzer bir hata oluşturmak için fromJustbir olduğunda Nothing, ancak bu işlev, muhtemelen hoş karşılanmaz).
Andres F.

2
"değeri dış dünyadan gelen bir tam sayı" - IO Integer'yürütüldüğünde tamsayı veren alt programa' daha yakın olmaz mıydı ? A) olarak main = c >> c, ilk olarak döndürülen değeri c, ikinci tarafından daha sonra farklı olabilir csüre ab) sanatisation zorlamak için dış dünyadan değerleri gösterir türleri vardır) sürece biz tek bir kapsamda olduğu gibi (ne olursa olsun konumunun aynı değere sahip olacaktır (yani doğrudan yerleştirmeyin, ancak ilk önce kullanıcıdan gelen girişin doğru olup olmadığını kontrol edin).
Maciej Piechotka

4
Maciej, bu daha doğru olurdu. Sadelik için çabalıyordum.
WolfeFan

30

Birçok insan Haskell hakkında güzel şeyler listeledi. Ancak “tip sistemi neden programları daha doğru hale getirir?” Sorusuna cevaben, cevabın “parametrik polimorfizm” olduğundan şüpheleniyorum.

Aşağıdaki Haskell işlevini göz önünde bulundurun:

foobar :: x -> y -> y

Orada kelimenin tam anlamıyla tek bir olası yolu bu fonksiyonu uygulamak için. Sadece tip imza, anlarım tam sadece bir olası şey olduğundan, bu işlevin ne olabilir yapmak. [Tamam, tam olarak değil, ama neredeyse!]

Dur ve bunu bir an için düşün. Bu aslında gerçekten büyük bir anlaşma! Bu imzayla bir işlev yazarsam, işlevin istediğimden başka bir şey yapması imkansız demektir . (Tabi imzanın kendisi hala yanlış olabilir, elbette. Hiçbir programlama dili tüm hataları önleyemez .)

Bu işlevi göz önünde bulundurun:

fubar :: Int -> (x -> y) -> y

Bu işlev mümkün değil . Kelimenin tam anlamıyla bu işlevi uygulayamazsınız. Bunu sadece tip imzasından söyleyebilirim.

Gördüğünüz gibi, bir Haskell tipi imza size çok şey anlatıyor!


C # ile karşılaştırın. (Üzgünüm, Java'm biraz paslanmış.)

public static TY foobar<TX, TY>(TX in1, TY in2)

Bu yöntemin yapabileceği birkaç şey var:

  • in2Sonuç olarak geri dönün .
  • Sonsuza dek döngü yapın ve hiçbir şey döndürmeyin.
  • Bir istisna at ve hiçbir şey döndürme.

Aslında Haskell'in da bu üç seçeneği var. Ancak C # size ek seçenekler de sunar:

  • Boş ver. (Haskell boş değil.)
  • Dönmeden in2önce değiştirin . (Haskell yerinde değişiklik yapmaz.)
  • Yansıma kullanın. (Haskell'in yansıması yoktur.)
  • Bir sonuç döndürmeden önce birden fazla G / Ç işlemi gerçekleştirin. (Burada G / Ç yaptığınızı beyan etmediğiniz sürece Haskell, G / Ç gerçekleştirmenize izin vermez.)

Yansıma, özellikle büyük bir çekiç; yansımayı kullanarak TYince havadan yeni bir nesne oluşturabilir ve onu geri getirebilirim ! Her iki nesneyi de inceleyebilir ve ne bulduğuma bağlı olarak farklı işlemler yapabilirim. İçeri aktarılan her iki nesnede de keyfi değişiklikler yapabilirim.

G / Ç benzer şekilde büyük bir çekiç. Kod kullanıcıya mesaj görüntülüyor ya da veri tabanı bağlantılarını açıyor ya da sabit diskinizi ya da herhangi bir şeyi gerçekten yeniden biçimlendiriyor olabilir.


Haskell foobarişlevi, aksine, yalnızca biraz veri alabilir ve bu verileri değiştirmeden geri verebilir . Derleme zamanında türü bilinmediğinden verilere "bakamaz". Yeni veri yaratamaz, çünkü ... peki, herhangi bir olası türde veriyi nasıl oluşturursunuz? Bunun için yansıma gerekir. Herhangi bir G / Ç gerçekleştiremez, çünkü tür imzası G / Ç'nin gerçekleştirildiğini bildirmez. Bu yüzden dosya sistemiyle veya ağla etkileşime giremez, hatta aynı programdaki iş parçacıklarını çalıştırabilir! (Yani,% 100 garantili iplik güvenlidir.)

Gördüğünüz gibi, tarafından değil sen şeyler bir sürü yapmasına izin, Haskell size kod aslında ne hakkında çok güçlü garantiler izin veriyor. Öyle ki, gerçekte, (gerçekten polimorfik kod için), parçaların birbirine uyması için genellikle tek bir yol vardır.

(Açık olmak gerekirse: tür imzasının size fazla bir şey söylemediği Haskell işlevlerini yazmak hala mümkündür. Neredeyse Int -> Inthiçbir şey olabilir. Ancak o zaman bile aynı girişin her zaman aynı çıktıyı% 100 kesinlikte üreteceğini biliyoruz. Java bile bunu garanti etmiyor!)


4
+1 Harika cevap! Bu çok güçlüdür ve çoğu zaman Haskell'e yeni gelenler tarafından takdir edilmez. Bu arada, daha basit bir "imkansız" işlevi olurdu fubar :: a -> b, değil mi? (Evet, farkındayım unsafeCoerce. Adında "güvensiz" olan hiçbir şeyden bahsetmediğimizi farz ediyorum ve yeni gelenler de bu konuda endişelenmemeli
Andres F.

Yazamadığınız birçok basit tip imza var, evet. Örneğin foobar :: x, oldukça uygulanamaz ...
MathematicalOrchid

Aslında, olamaz saf kod parçacığı güvensiz hale, ama yine de çok kanallı yapabilirsiniz. Seçenekleriniz "bunu değerlendirmeden önce, bunu değerlendir" "," bunu değerlendirirken, bunu ayrı bir dizide de değerlendirmek isteyebilirsiniz "ve" bunu değerlendirirken bunu da değerlendirmek isteyebilirsiniz, belki de ayrı bir konuya ". Varsayılan ayar, "istediğiniz gibi yapın" anlamına gelir; bu, temel olarak "mümkün olduğunca geç değerlendirme" anlamına gelir.
John Dvorak

Daha kesin olarak, in1 veya in2'de yan etkileri olan örnek yöntemleri çağırabilirsiniz. Ya da Haskell'de bir IO eylemi olarak modellenen (ancak çoğu insanın IO olarak düşündüğü şey olmayabilir) olan küresel durumu değiştirebilirsiniz.
Doug McClean

2
@ izomorfizmi Tip x -> y -> ymükemmel bir şekilde uygulanabilir. Tip (x -> y) -> ydeğil. Tür x -> y -> yiki giriş alır ve ikincisini döndürür. Bu tip çalışan bir fonksiyon(x -> y) -> y alır ve bir şekilde bundan kurtulmak zorundadır ...xy
MathematicalOrchid

17

İlgili bir SO sorusu .

Aynı şeyi haskell'de yapabileceğinizi varsayıyorum (yani, gerçekten birinci sınıf veri türleri olması gereken karakterleri / karakterleri doldurun)

Hayır, gerçekten yapamazsınız - en azından Java'nın yapacağı şekilde değil. Java'da bu tür şeyler olur:

String x = (String)someNonString;

Java, String olmayan bir dizginizi mutlu bir şekilde dener ve kullanır. Haskell, bu tür bir şeye izin vermez ve tüm çalışma zamanı hataları sınıfını ortadan kaldırır.

nulltür sisteminin bir parçası olduğu için Nothingdiğer tüm çalışma zamanı hataları sınıfını ortadan kaldırarak açıkça sorulması ve kullanılması gerekir.

İletişim kurmak için yeterince iyi bilecek uzmanlığa sahip olmadığım - özellikle yeniden kullanım ve tip sınıfları etrafında - bir sürü ince yarar da var.

Çoğunlukla, bunun nedeni Haskell'in tip sisteminin çok fazla ifadeye izin vermesidir. Sadece birkaç kuralla bir sürü şey yapabilirsin. Şimdiye kadarki Haskell ağacını düşünün:

data Tree a = Leaf a | Branch (Tree a) (Tree a) 

Genel bir ikili ağacı (ve iki veri yapıcısı) oldukça okunabilir bir kod satırında tanımladınız. Hepsi sadece birkaç kural kullanarak ( toplam tür ve ürün tipinde ). Java'daki 3-4 kodlu dosya ve sınıflardır.

Özellikle tip sistemlerini revize etmeye eğilimli olanlar arasında, bu tür bir özlülük / zerafet çok değerlidir.


Cevabınızı sadece NullPointerExceptions anladım. Daha fazla örnek ekler misiniz?
Jesvin Jose

2
Mutlaka doğru değil, JLS §5.5.1 : Eğer T bir sınıf tipi ise, | S | <: | T |, veya | T | <: | S |. Aksi takdirde, bir derleme zamanı hatası oluşur. Bu nedenle derleyici, dönüştürülemez türler yayınlamanıza izin vermez - bunun etrafında bir yol vardır.
Örümcek Boris,

Bence tip sınıflarının avantajlarını ortaya koymanın en basit yolu, gerçeklerden interfacesonra eklenebilecekleri gibidir ve onları uygulayan türü "unutmaz". Yani, bir fonksiyona iki argümanın aynı tipte olmasını sağlayabilirsiniz, interfaces nin aksine iki List<String>s farklı uygulamalara sahip olabilir. Her arabirime bir tür parametresi ekleyerek teknik olarak Java'da çok benzer bir şey yapabilirsiniz, ancak varolan arabirimlerin% 99'u bunu yapmaz ve meslektaşlarınızın kafasını karıştırırsınız.
Doval,

2
@BoristheSpider Doğru, ancak istisnalar yayınlamak neredeyse her zaman bir üst sınıftan bir alt sınıfa veya bir arayüzden bir sınıfa indirme yapmayı içerir ve üst sınıfın olması alışılmadık bir durum değildir Object.
Doval,

2
Dizeler ile ilgili soruların amacı, döküm ve çalışma zamanı tür hatalarıyla ilgili değil, türleri kullanmak istemiyorsanız Java'nın aslında sizi, verilerinizi serileştirilmiş olarak depolamaktan kurtarmayacağını düşünüyorum. bir geçici anytür olarak dizeleri kötüye kullanma . Haskell de bunu yapmanı engellemeyecek, çünkü ... peki, dizeleri var. Haskell zorla olamaz, araçlar olabilir size durdurmak Eğer yeniden icat etmek bir tercümanın Greenspunning yeterli uzunlukta ısrar aptalca şeyler yapmaktan nulliç içe bir bağlamda. Hiçbir dil yapamaz.
Leushenko

0

Tanıdığı insanların sıkça konuştukları 'mem'lerden biri, "derlerse işe yarar *" meselesidir - bence bu, tip sisteminin gücüyle ilgili olduğunu düşünüyorum.

Bu çoğunlukla küçük programlarda geçerlidir. Haskell, diğer dillerde kolay olan hatalar yapmanıza engel olur (örneğin, bir Int32ve bir Word32şeyler patlar), ancak sizi tüm hatalardan engellemez.

Haskell aslında yeniden düzenlemeyi çok daha kolay hale getiriyor . Programınız daha önce doğru olsaydı ve denetlerse, küçük emisyonlardan sonra hala doğru olma olasılığı oldukça yüksektir.

Neden tam olarak Haskell'in bu konuda diğer statik olarak yazılmış dillerden daha iyi olduğunu anlamaya çalışıyorum.

Haskell'deki tipler oldukça hafiftir, bu nedenle yeni tipler tanımlamak kolaydır. Bu her şeyin biraz daha hantal olduğu Rust gibi bir dilin aksine.

Benim varsayım, insanların güçlü bir tip sistemle kastettiği şeydir, ancak Haskell'in neden daha iyi olduğu bana açık değil.

Haskell, basit toplamın ve ürün türlerinin ötesinde birçok özelliğe sahiptir; evrensel olarak ölçülmüş tiplere de (örneğin id :: a -> a) sahiptir. Java veya Rust gibi bir dilden oldukça farklı işlevler içeren kayıt türleri de oluşturabilirsiniz.

GHC, yalnızca türlere göre bazı örnekler de türetebilir ve jeneriklerin ortaya çıkmasından bu yana, türler arasında genel olan işlevleri yazabilirsiniz. Bu oldukça kullanışlıdır ve Java'dakinden daha akıcıdır.

Başka bir fark, Haskell'in nispeten iyi tip hatalara sahip olma eğiliminde olmasıdır (en azından yazılı olarak). Haskell'in tipi çıkarımı karmaşıktır ve derlenecek bir şey almak için yazım açıklamaları girmeniz gerekmesi oldukça nadirdir. Bu, tür çıkarımının, derleyici türü ilke olarak çıkarsa bile, bazen ek açıklamalar gerektirebileceği Rust ile zıttır.

Sonunda, Haskell'in da kendi aralarında ünlü monad olan sınıfları var. Monad'lar hataları ele almanın özellikle iyi bir yolu olur; temel olarak null, korkunç hata ayıklama olmadan ve türünüzün güvenliğini bırakmadan neredeyse tüm rahatlığı sağlarlar . Bu yüzden bu tipler üzerinde fonksiyon yazabilmek , onları kullanmaya teşvik etmemiz konusunda aslında biraz önemlidir!

Başka bir deyişle, iyi ya da kötü Java yazabilir, Haskell'de de aynı şeyi yapabileceğinizi varsayıyorum.

Bu belki de doğrudur, ancak çok önemli bir nokta eksik: Haskell'de kendinizi ayağınıza çekmeye başladığınız nokta Java'da kendinizi ayağınıza çekmeye başladığınız noktadan daha ileride.

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.