Haskell'de "Just" sözdizimi ne anlama geliyor?


118

Bu anahtar kelimenin ne işe yaradığına dair gerçek bir açıklama için interneti araştırdım. Baktığım her Haskell öğreticisi onu rastgele kullanmaya başlıyor ve hiçbir zaman ne işe yaradığını açıklamıyor (ve pek çoğuna baktım).

İşte Real World Haskell'in kullandığı temel bir kod parçası Just. Kodun ne yaptığını anlıyorum, ancak amacının veya işlevinin ne olduğunu anlamıyorum Just.

lend amount balance = let reserve    = 100
                      newBalance = balance - amount
                  in if balance < reserve
                     then Nothing
                     else Just newBalance

Gözlemlediğim kadarıyla, Maybeyazı yazmakla ilgili , ama öğrendiğim neredeyse hepsi bu.

Ne Justanlama geldiğine dair iyi bir açıklama çok takdir edilecektir.

Yanıtlar:


211

Aslında, Prelude'da tanımlanan normal bir veri yapıcısıdır. , her modüle otomatik olarak içe aktarılan standart kitaplık olan .

Yapısal Olarak Belki Nedir?

Tanım şuna benzer:

data Maybe a = Just a
             | Nothing

Bu bildirim, bir tür Maybe adeğişkeni tarafından parametrelendirilen bir türü tanımlar , bu da aonu herhangi bir türle yerine kullanabileceğiniz anlamına gelir.a .

İnşa Etmek ve Yıkmak

Türün iki kurucusu vardır Just ave Nothing. Bir türün birden fazla kurucusu olduğunda, bu, türün bir değerinin olası kuruculardan yalnızca biriyle oluşturulmuş olması gerektiği anlamına gelir. Bu tür için, bir değer ya aracılığıyla oluşturulmuştur Justya da Nothingbaşka (hatasız) olasılık yoktur.

Yana NothingYapıcı o isimler türde bir üyesi olan bir sabit değer olarak kullanılan zaman, hiçbir parametre türü olan Maybe aher türlü a. Fakat Justyapıcı bir yapıcı olarak kullanıldığında, tip bir işlev gibi hareket araçlarının bir tür parametresi, var aiçin Maybe ayani türü vardır,a -> Maybe a

Dolayısıyla, bir türün kurucuları bu türden bir değer oluşturur; işlerin diğer tarafı, bu değeri kullanmak istediğiniz zamandır ve işte burada kalıp eşleme devreye girer. İşlevlerin aksine, yapıcılar desen bağlama ifadelerinde kullanılabilir ve bu, birden fazla yapıcıya sahip türlere ait değerlerin durum analizini yapmanın yoludur .

Maybe aBir kalıp eşleşmesinde bir değer kullanmak için, her kurucu için aşağıdaki gibi bir kalıp sağlamanız gerekir:

case maybeVal of
    Nothing   -> "There is nothing!"
    Just val  -> "There is a value, and it is " ++ (show val)

Bu durumda ifadede, değer olsaydı ilk desen eşleşirdi ve değer Nothingile oluşturulmuşsa ikincisi eşleşirdi Just. İkincisi eşleşirse, aynı zamanda adı val, iletilen parametreye de bağlar .Just eşleştirdiğiniz değer oluşturulduğunda .

Belki Ne Anlama Gelir

Belki bunun nasıl çalıştığını zaten biliyordunuz; Maybedeğerler için gerçekten bir sihir yoktur , bu sadece normal bir Haskell Cebirsel Veri Türüdür (ADT). Ancak, bir türü, Integerörneğinizden olduğu gibi, değer Nothingeksikliğini temsil eden ekstra bir değere ( ) sahip olduğu yeni bir bağlama etkili bir şekilde "yükselttiği" veya genişlettiği için oldukça kullanılır ! Tür sistemi o zaman almak izin önce bu ekstra değer denetlemek gerektirir Integero olabilir orada. Bu, dikkate değer sayıda hatayı önler.

Günümüzde pek çok dil, bu tür "değer içermeyen" değerleri NULL referanslar aracılığıyla ele almaktadır. Seçkin bir bilgisayar bilimcisi olan Tony Hoare (Quicksort'u icat etti ve Turing Ödülü sahibi), bunu "milyar dolarlık hatası" olarak kabul ediyor . Belki türü bunu düzeltmenin tek yolu değildir, ancak bunu yapmanın etkili bir yolu olduğu kanıtlanmıştır.

Belki bir Functor olarak

Bir türü diğerine dönüştürme fikri, eski türdeki işlemlerin de yeni tür üzerinde çalışacak şekilde dönüştürülebilmesi fikri Functor, Maybe ayararlı bir örneğine sahip olan Haskell türü sınıfının arkasındaki kavramdır .

Functorfmaptemel türdeki değerlere göre (örneğin Integer), yükseltilmiş türdeki değerlere göre değişen işlevlere (örneğin ) eşleyen işlevleri eşleyen bir yöntem sağlar Maybe Integer. Bir değer fmapüzerinde çalışmak üzere dönüştürülen bir işlev şu şekilde çalışır Maybe:

case maybeVal of
  Nothing  -> Nothing         -- there is nothing, so just return Nothing
  Just val -> Just (f val)    -- there is a value, so apply the function to it

Dolayısıyla, bir Maybe Integerdeğeriniz m_xve bir Int -> Intfonksiyonunuz fvarsa, gerçekten bir değer alıp almadığına dair endişelenmeden fmap f m_xfonksiyonu fdoğrudan fonksiyona uygulayabilirsiniz Maybe Integer. Aslında, değerlere bütün bir kaldırılmış Integer -> Integerişlevler zinciri uygulayabilirsiniz Maybe Integerve Nothingişiniz bittiğinde yalnızca bir kez açıkça kontrol etme konusunda endişelenmeniz gerekir .

Belki bir Monad olarak

MonadHenüz a kavramına ne kadar aşina olduğunuzdan emin değilim , ancak en azından daha IO aönce kullandınız ve yazı tipi imzası ile IO aoldukça benzer görünüyor Maybe a. Yapıcılarını IOsize göstermemesi ve bu nedenle yalnızca Haskell çalışma zamanı sistemi tarafından "çalıştırılabilmesi" açısından özel olmasına rağmen , yine de Functorbir Monad. Aslında, a'nın bazı ekstra özelliklere sahip Monadözel bir tür olduğu konusunda önemli bir anlam var Functor, ancak bu konuya girilecek yer burası değil.

Her neyse, Monadlar IOeşleme türlerini "değerlerle sonuçlanan hesaplamaları" temsil eden yeni türlere sever ve normal bir işlevi "hesaplamaya dönüştüren ve normal bir işlevi" hesaplamayla sonuçlanan "hesaplamaya dönüştüren Monadçok benzer bir fmapişlev aracılığıyla işlevleri türlere kaldırabilirsiniz liftM. işlevi."

Muhtemelen tahmin etmişsinizdir (şimdiye kadar okuduysanız) bunun Maybeda bir Monad. "Bir değer döndürmede başarısız olabilecek hesaplamaları" temsil eder. Aynı fmapörnekte olduğu gibi , bu, her adımdan sonra hataları açıkça kontrol etmek zorunda kalmadan bir dizi hesaplama yapmanıza olanak tanır. Ve aslında, Monadörneğin inşa edilme şekli, Maybedeğerlerle ilgili bir hesaplama, bir ile karşılaşıldığı anda dururNothing , bu nedenle bu, bir hesaplamanın ortasında bir ani iptal veya değersiz bir geri dönüş gibidir.

Belki Yazabilirdin

Daha önce de söylediğim gibi Maybe, dil sözdizimi veya çalışma zamanı sistemine eklenen türden hiçbir şey yoktur . Haskell bunu varsayılan olarak sağlamadıysa, tüm işlevlerini kendiniz sağlayabilirsiniz! Aslında, onu yine de farklı isimlerle tekrar yazabilir ve aynı işlevselliği elde edebilirsiniz.

Umarım Maybetürü ve yapıcılarını şimdi anlarsınız , ancak hala net olmayan bir şey varsa, bana bildirin!


19
Ne olağanüstü bir cevap! Haskell'in sık sık Maybediğer dillerin kullanılacağı yerlerde kullandığı nullveya nil( NullPointerExceptionher köşede kötü sinsice gizlendiği) belirtilmelidir. Şimdi diğer diller de bu yapıyı kullanmaya başlıyor: Scala as Optionve hatta Java 8 bile Optionaltüre sahip olacak .
LANDEI

3
Bu mükemmel bir açıklamadır. Okuduğum açıklamaların çoğu, Just'un Belki tipi için bir kurucu olduğu fikrine ipucu veriyordu, ancak hiçbiri bunu gerçekten açık hale getirmedi.
Reem

@ Landei, öneri için teşekkürler. Boş referansların tehlikesine atıfta bulunmak için bir düzenleme yaptım.
Levi Pearson

@Landei Seçenek türü 70'lerde ML'den beri ortalıktaydı, Scala'nın onu oradan almış olması daha muhtemel çünkü Scala ML'nin adlandırma kuralını kullanıyor, Option kurucularla bir miktar ve hiçbiri.
stonemetal

@Landei Apple'ın Swift biraz da isteğe bağlı öğeleri kullanır
Jamin

37

Mevcut cevapların çoğu, Justarkadaşların nasıl ve nasıl çalıştığına dair oldukça teknik açıklamalar ; Bunun ne için olduğunu açıklamaya çalışabileceğimi düşündüm.

Çoğu dilin null, en azından bazı türler için gerçek bir değer yerine kullanılabilecek bir değeri vardır. Bu, birçok insanı çok kızdırdı ve yaygın olarak kötü bir hareket olarak görüldü. Yine de, nullbir şeyin yokluğunu belirtmek gibi bir değere sahip olmak bazen yararlıdır .

Haskell bu sorunu, sahip olabileceğiniz yerleri açıkça işaretleyerek çözer Nothing(a sürümü null). Temel olarak, işleviniz normalde türü döndürürse Foo, bunun yerine türü döndürmelidir Maybe Foo. Değer olmadığını belirtmek istiyorsanız geri dönün Nothing. Bir değer döndürmek baristiyorsanız, bunun yerine dönmelisiniz Just bar.

Yani temelde, sahip Nothingolamıyorsanız, ihtiyacınız yok Just. Eğer yapabilirsen Nothing, ihtiyacın var Just.

Büyülü bir şey yok Maybe; Haskell tipi sistem üzerine inşa edilmiştir. Bu, onunla tüm olağan Haskell kalıbı eşleştirme hilelerini kullanabileceğiniz anlamına gelir .


1
Çok güzel diğer cevaplar yanındaki cevabı, ama yine de :) bir kod örneği yararlanacak düşünüyorum
PascalVKooten

13

Bir tip göz önüne alındığında t, bir değer Just ttipte bir mevcut değer olan t, Nothingbir değere ulaşmak için bir hata ya da bir değere sahip anlamsız olacaktır bir durumu temsil etmektedir.

Örneğinizde, negatif bir bakiyeye sahip olmak bir anlam ifade etmiyor ve bu nedenle, böyle bir şey meydana gelirse, bunun yerini alır Nothing.

Başka bir örnek için, bu, bölme işleminde, alan ave alan ve sıfır değilse bgeri dönen bir bölme işlevini tanımlayarak kullanılabilir . İstisnalara uygun bir alternatif olarak veya daha önceki örneğiniz gibi, mantıklı olmayan değerleri değiştirmek için genellikle bu şekilde kullanılır.Just a/bbNothing


1
Öyleyse yukarıdaki kodda Adalet'i kaldırdım diyelim, bu neden çalışmasın? Bir Hiçbir Şeye sahip olmak istemediğin her an sahip olmak zorunda mısın? İfadenin içindeki bir işlevin Hiçbir şey döndürmediği bir ifadeye ne olur?
2013

7
JustÖğesini kaldırdıysanız, kodunuz onay yazmaz. Bunun nedeni, Justuygun türleri korumaktır. Maybe tFormun öğelerinden oluşan bir tip (aslında bir monad, ama sadece bir tip olarak düşünmek daha kolaydır) Just tve Nothing. NothingTür olduğundan , tür değerlerinden Maybe tbirini Nothingveya bir kısmını değerlendirebilen bir ifade tdoğru şekilde yazılmaz. NothingBazı durumlarda bir işlev geri dönerse , bu işlevi kullanan herhangi bir ifadenin isJust, tüm olası durumları ele almak için bunu ( veya bir durum ifadesini) kontrol etmenin bir yolu olmalıdır .
qaphla

2
Yani Just, Belki türünde tutarlı olmak için oradadır çünkü normal bir t Belki türünde değildir. Artık her şey çok daha net. Teşekkür ederim!
2013

3
@qaphla: Bunun "aslında bir monad, [...]" olduğu hakkındaki yorumunuz yanıltıcı. Maybe t olduğu bir türü. Bir Monadörneğinin olması Maybeonu tip olmayan bir şeye dönüştürmez.
Sarah

2

A-> b toplam işlevi, a türünün olası her değeri için b türünde bir değer bulabilir.

Haskell'de tüm işlevler toplam değildir. Bu özel durumda fonksiyon lendtoplam değildir - bakiyenin rezervden daha az olduğu durum için tanımlanmamıştır (yine de, benim zevkime göre newBalance'ın rezervden daha az olmasına izin vermemek daha mantıklı olacaktır - olduğu gibi, 101'i ödünç alabilirsiniz. 100'lük bir denge).

Toplam olmayan işlevlerle ilgilenen diğer tasarımlar:

  • giriş değerinin aralığa uymadığını kontrol ettikten sonra istisnalar atın
  • özel bir değer döndürme (ilkel tür): sık kullanılan seçim, Doğal sayıları döndürmesi amaçlanan tamsayı işlevleri için negatif bir değerdir (örneğin, String.indexOf - bir alt dize bulunamadığında, döndürülen dizin genellikle negatif olacak şekilde tasarlanmıştır)
  • özel bir değer (işaretçi) döndür: NULL veya benzeri
  • hiçbir şey yapmadan sessizce geri dönün: örneğin, lendödünç verme koşulu karşılanmazsa eski bakiyeyi döndürmek için yazılabilir
  • özel bir değer döndür: Hiçbir şey (veya bazı hata açıklama nesnelerini sola kaydırma)

Bunlar, işlevlerin bütünlüğünü zorlayamayan dillerde gerekli tasarım sınırlamalarıdır (örneğin, Agda yapabilir, ancak bu, tamamlanmamış olma gibi başka komplikasyonlara yol açar).

Özel bir değer döndürme veya istisnalar atma ile ilgili sorun, arayanın yanlışlıkla böyle bir olasılığın ele alınmasını atlamasının kolay olmasıdır.

Bir arızanın sessizce atılmasındaki sorun da açıktır - arayanın işlevle neler yapabileceğini sınırlamış oluyorsunuz. Örneğin, lendeski bakiye döndürülürse, arayanın dengenin değişip değişmediğini bilme yolu yoktur. Amaçlanan amaca bağlı olarak bir sorun olabilir veya olmayabilir.

Haskell'in çözümü, kısmi bir işlevin arayanı benzer türle Maybe aveya Either error aişlevin dönüş türü nedeniyle ilgilenmeye zorlar .

Bu şekilde lendtanımlandığı gibi, her zaman yeni dengeyi hesaplamayan bir işlevdir - bazı durumlarda yeni denge tanımlanmaz. Bu durumu arayan kişiye ya Nothing özel değerini iade ederek ya da yeni bakiyeyi Just'a kaydırarak bildiririz. Arayanın artık seçim yapma özgürlüğü var: ya ödünç vermedeki başarısızlığı özel bir şekilde ele alın ya da eski bakiyeyi görmezden gelin ve kullanın - örneğin maybe oldBalance id $ lend amount oldBalance,.


-1

İşlev if (cond :: Bool) then (ifTrue :: a) else (ifFalse :: a)aynı ifTrueve türüne sahip olmalıdır ifFalse.

Biz yazdığınızda, then Nothingbiz kullanmalısınız Maybe aiçinde türünüelse f

if balance < reserve
       then (Nothing :: Maybe nb)         -- same type
       else (Just newBalance :: Maybe nb) -- same type

1
Eminim burada gerçekten derin bir şey söylemek
istediniz

1
Haskell'de tür çıkarımı vardır. Tipini Nothingve Just newBalanceaçıkça belirtmeye gerek yoktur .
2013

Deneyimsizlere yapılan bu açıklama için, açık tipler kullanımının anlamını netleştirir Just.
philip
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.