İşte Haskell nasıl yapıyor: (Haskell Nesneye Yönelik bir dil olmadığından Lippert'in ifadelerine tam olarak uymaz).
UYARI: Önümüzdeki ciddi bir Haskell fanatiğinden uzun soluklu cevap.
TL; DR
Bu örnek Haskell'in C # 'dan ne kadar farklı olduğunu göstermektedir. Yapı inşaatının lojistiğini bir kurucuya devretmek yerine, çevre kodunda ele alınmalıdır. Bir boş (veya Nothing
Haskell'de) değer değerinin boş olmayan bir değer beklediğimiz yerde kesmesine imkan yoktur, çünkü boş değerler yalnızca Maybe
doğrudan, doğrudan normalden dönüştürülebilen ve normalden dönüştürülemeyen özel sargılı türlerde oluşabilir. null türleri N'ye sarılarak null yapılabilen bir değeri kullanmak için Maybe
, önce kontrol eşlemesini boş olmayan bir değere sahip olduğumuzu bildiğimiz bir dallamaya yönlendirmeye zorlayan, kalıp eşleştirme kullanarak değeri çıkarmamız gerekir.
Bu nedenle:
Null edilemeyen bir referansın hiçbir zaman geçersiz olduğu gözlemlenen koşullar altında olmadığını her zaman bilebilir miyiz?
Evet. Int
ve Maybe Int
iki tamamen ayrı tipler. Bulma Nothing
bir ovada Int
bir dize "balık" bulma karşılaştırılabilir olacaktır Int32
.
NULL olmayan bir referans türü olan bir nesnenin yapıcısına ne demeli?
Sorun değil: Haskell'deki değer yapıcıları hiçbir şey yapamazlar, verilen değerleri alıp bir araya getirirler. Tüm başlatma mantığı, yapıcı çağrılmadan önce gerçekleşir.
Peki ya referansı doldurması gereken kod bir istisna attığı için böyle bir nesnenin sonlandırıcısında, nesnenin sonlandırıldığı yerde?
Haskell'de kesinleştiriciler yok, bu yüzden gerçekten buna değinemiyorum. Ancak ilk cevabım hala geçerli.
Tam Cevap :
Haskell'in boş değeri yoktur ve boş Maybe
alanları temsil etmek için veri türünü kullanır . Belki de bunun gibi tanımlanmış bir cebirsel veri türüdür :
data Maybe a = Just a | Nothing
Haskell'ı tanımayanlar için bunu "A Maybe
ya a Nothing
ya da a Just a
" olarak okuyunuz . özellikle:
Maybe
olduğu bir tipte : (bir genel sınıf olarak (yanlış) düşünülebilir a
tipi değişkendir). C # analojisidir class Maybe<a>{}
.
Just
bir değer yapıcısı : bir tür argümanı alan a
ve değeri Maybe a
içeren bir tür değeri döndüren bir işlevdir . Yani kod x = Just 17
benzer int? x = 17;
.
Nothing
başka bir değer kurucu, ancak hiçbir argüman almaz ve Maybe
geri dönen, "Hiçbirşey" den başka bir değere sahip değildir. x = Nothing
benzerdir int? x = null;
(biz bizim kısıtlı varsayarak a
Haskell olmak Int
yazılı yapılabilir, hangi x = Nothing :: Maybe Int
).
Şimdi, Maybe
türün temelleri ortadan kalktığına göre Haskell, OP'nin sorusunda tartışılan sorunları nasıl önler?
Haskell, şu ana kadar tartışılan dillerin çoğundan gerçekten farklı, bu yüzden birkaç temel dil ilkesini açıklayarak başlayacağım.
Öncelikle, Haskell'de her şey değişmez . Her şey. Adlar, değerlerin saklanabileceği yerlere değil, değerlere atıfta bulunur (bu yalnızca büyük bir hata giderme kaynağıdır). Değişken bildirim ve atama değerlerini tanımlayarak oluşturulan Haskell değerlerinde iki ayrı operasyon vardır C #, aksine (örneğin x = 15
, y = "quux"
, z = Nothing
), asla değiştiremezsin ki. Bu nedenle, kod gibi:
ReferenceType x;
Haskell'de mümkün değil. Değerlerin başlatılması ile ilgili hiçbir sorun yoktur, null
çünkü her şeyin var olması için açıkça bir değere başlatılması gerekir.
İkincisi, Haskell nesne yönelimli bir dil değildir : tamamen işlevsel bir dildir, dolayısıyla kelimenin tam anlamıyla hiçbir nesne yoktur. Bunun yerine, argümanlarını alan ve birleştirilen bir yapı döndüren işlevler (değer yapıcıları) vardır.
Sonra, kesinlikle bir zorunlu stil kodu yoktur. Bununla, çoğu dilin böyle bir örüntü izlediğini kastediyorum:
do thing 1
add thing 2 to thing 3
do thing 4
if thing 5:
do thing 6
return thing 7
Program davranışı bir dizi talimat olarak ifade edilir. Nesne Yönelimli dillerde, sınıf ve işlev bildirimleri de program akışında büyük rol oynar, ancak esastır, bir programın yürütülmesinin "eti" yürütülmesi gereken bir dizi talimat biçimini alır.
Haskell'de bu mümkün değildir. Bunun yerine, program akışı tamamen zincirleme işlevleriyle belirlenir. Emir do
kuvveti arayan gösterim bile isimsiz işlevleri >>=
operatöre aktarmak için sadece sözdizimsel bir şeker . Tüm fonksiyonlar şu şekildedir:
<optional explicit type signature>
functionName arg1 arg2 ... argn = body-expression
body-expression
Bir değeri değerlendiren herhangi bir şey nerede olabilir. Açıkçası daha fazla sözdizimi özellikleri mevcut ancak ana nokta, ifadelerin dizisinin tam olmamasıdır.
Son olarak, ve muhtemelen en önemlisi, Haskell'in tür sistemi inanılmaz derecede katıdır. Haskell'in tip sisteminin merkezi tasarım felsefesini özetlemek zorunda olsaydım derdim: "Derleme zamanında mümkün olduğunca çok şeyin yanlış gitmesini sağlayın, böylece çalışma zamanında olabildiğince az yanlış yapın." Hiçbir örtük dönüşüm (bir tanıtmak isteyen var Int
a Double
? Kullanın fromIntegral
fonksiyonu). Çalışma zamanında meydana gelen geçersiz bir değere sahip olan tek şey kullanmaktır Prelude.undefined
(görünüşe göre sadece orada olması ve kaldırılması imkansızdır ).
Bütün bunlar göz önünde bulundurularak, Amon'un "kırık" örneğine bakalım ve bu kodu Haskell'de tekrar ifade etmeye çalışalım. İlk olarak, veri bildirimi (adlandırılmış alanlar için kayıt sözdizimini kullanarak):
data NotSoBroken = NotSoBroken {foo :: Foo, bar :: Bar }
( foo
ve bar
gerçekten asıl alanlar yerine anonim alanlara erişim işlevi görür, ancak bu ayrıntıyı yok sayabiliriz).
NotSoBroken
Değeri yapıcı bir alma dışında herhangi bir işlem alma yeteneğinde olmayan Foo
ve Bar
(null değildir) ve bir alma NotSoBroken
bunların out. Zorunlu kod koymak veya hatta alanları manuel olarak atamak için yer yoktur. Tüm başlatma mantığı, başka bir yerde, büyük olasılıkla özel bir fabrika fonksiyonunda gerçekleşmelidir.
Örnekte, Broken
her zaman yapılaşma başarısız olur. NotSoBroken
Değer yapıcısını benzer bir şekilde kırmanın bir yolu yoktur (kodu yazacak hiçbir yer yoktur), ancak benzer şekilde hatalı olan bir fabrika işlevi oluşturabiliriz.
makeNotSoBroken :: Foo -> Bar -> Maybe NotSoBroken
makeNotSoBroken foo bar = Nothing
(ilk satır bir tür imza bildirimidir: makeNotSoBroken
a Foo
ve Bar
a'yı argüman olarak alır ve a üretir Maybe NotSoBroken
).
Geri dönüş tipi, Maybe NotSoBroken
basitçe NotSoBroken
değerlendirmek zorunda olduğumuz için değil Nothing
, bunun için bir değer yapıcısı olduğu söylenmelidir Maybe
. Farklı bir şey yazsaydık, türler sıraya girmezdi.
Kesinlikle anlamsız olmasının yanı sıra, bu işlev ne zaman kullanacağımızı göreceğimiz gibi asıl amacını bile yerine getirmiyor. Bir argüman olarak useNotSoBroken
bekleyen bir fonksiyon yaratalım NotSoBroken
:
useNotSoBroken :: NotSoBroken -> Whatever
( bir argümanı useNotSoBroken
kabul eder NotSoBroken
ve üretir Whatever
).
Ve bu şekilde kullanın:
useNotSoBroken (makeNotSoBroken)
Çoğu dilde, bu tür davranışlar boş gösterici istisnalarına neden olabilir. Haskell'de türler eşleşmiyor: a makeNotSoBroken
döndürür Maybe NotSoBroken
, ancak a useNotSoBroken
bekler NotSoBroken
. Bu türler birbirlerinin yerine geçemez ve kod derlenemez.
Bunu aşmak case
için Maybe
değerin yapısına dayanarak dallara yapılan bir ifade kullanabiliriz ( desen eşleme adı verilen bir özelliği kullanarak ):
case makeNotSoBroken of
Nothing -> --handle situation here
(Just x) -> useNotSoBroken x
Açıkçası, bu pasajın aslında derlemek için bir bağlamın içine yerleştirilmesi gerekiyor, ancak Haskell'in nullaları nasıl idare ettiğinin temellerini gösteriyor. Yukarıdaki kodun adım adım açıklaması:
- Birincisi,
makeNotSoBroken
bir tür değer üretmesi garanti edilen değerlendirilir Maybe NotSoBroken
.
case
İfadesi bu değerin yapısını inceler.
- Değer ise
Nothing
, "burada durum ele" kodu değerlendirilir.
- Değer, bunun yerine bir
Just
değerle eşleşirse, diğer dal yürütülür. Eşleşen maddenin eşzamanlı olarak değeri bir Just
yapı olarak nasıl tanımladığını ve iç NotSoBroken
alanını bir isme nasıl bağladığını not edin (bu durumda, x
). x
daha sonra normal NotSoBroken
değer gibi kullanılabilir .
Bu nedenle, desen eşleştirme, tür güvenliğini sağlamak için güçlü bir olanak sağlar, çünkü nesnenin yapısı kontrolün dallanmasına ayrılmaz bir şekilde bağlıdır.
Umarım bu anlaşılabilir bir açıklama olmuştur. Eğer mantıklı değilse, Büyük İyilik İçin Size Bir Haskell Öğrenin! , şimdiye kadar okuduğum en iyi çevrimiçi dil öğreticilerinden biri. İnşallah aynı güzelliği yaptığım bu dilde göreceksiniz.