Haskell bildirisinde ünlem işareti ne anlama geliyor?


262

Haskell'i sürmek için gerçek bir proje kullanarak öğrenmeye çalışırken aşağıdaki tanımla karşılaştım. Her bir argümanın önündeki ünlem işaretinin ne anlama geldiğini anlamıyorum ve kitaplarım bundan bahsetmiyor gibiydi.

data MidiMessage = MidiMessage !Int !MidiMessage

13
Bunun çok yaygın bir soru olabileceğinden şüpheleniyorum; Aynı şeyi kendim de merak ettiğimizi hatırlıyorum
cjs

Yanıtlar:


314

Bu bir katılık beyanı. Temel olarak, veri yapısı değeri yaratıldığında "zayıf kafa normal formu" olarak değerlendirilmesi gerektiği anlamına gelir. Bir örneğe bakalım, böylece bunun ne anlama geldiğini görebilelim:

data Foo = Foo Int Int !Int !(Maybe Int)

f = Foo (2+2) (3+3) (4+4) (Just (5+5))

İşlev f , değerlendirildiğinde bir "thunk" döndürür: yani değerini bulmak için çalıştırılacak kod. Bu noktada, bir Foo henüz mevcut değil, sadece kod.

Ancak bir noktada, biri muhtemelen bir desen eşleşmesi yoluyla içine bakmaya çalışabilir:

case f of
     Foo 0 _ _ _ -> "first arg is zero"
     _           -> "first arge is something else"

Bu, ihtiyaç duyduğu şeyi yapmak için yeterli kod yürütecek ve daha fazla olmayacak. Böylece dört parametreli bir Foo oluşturacaktır (çünkü mevcut olmadan içine bakamazsınız). Birincisi, test ettiğimiz için, aşağıdakileri değerlendirmek zorundayız:4 , eşleşmediğini fark ettiğimiz .

İkincisinin değerlendirilmesi gerekmez, çünkü test etmiyoruz. Bu nedenle, 6o bellek konumunda depolanmak yerine , kodu daha sonra olası değerlendirme için saklayacağız (3+3). Bu sadece birisi bakarsa 6'ya dönüşecektir.

Bununla birlikte, üçüncü parametrenin !önünde bir tane vardır, bu yüzden kesinlikle değerlendirilir: (4+4)yürütülür ve 8bu bellek konumunda saklanır.

Dördüncü parametre de kesinlikle değerlendirilir. Ama burada biraz zorlaşıyor: tam olarak değil, sadece zayıf normal kafa formunu değerlendiriyoruz. Bu, bunun Nothingya da bir Justşeyin olup olmadığını anladığımız ve sakladığımız anlamına gelir, ancak daha fazla ilerlemeyiz. Bu, depolamayı değerlendirmeden değil Just 10aslında sakladığımız anlamına gelir Just (5+5). Bunu bilmek önemlidir, ancak bunun tüm sonuçlarının bu sorunun kapsamının çok ötesine geçtiğini düşünüyorum.

BangPatternsDil uzantısını etkinleştirirseniz, işlev bağımsız değişkenlerine aynı şekilde açıklama ekleyebilirsiniz :

f x !y = x*y

f (1+1) (2+2)thunk dönecektir (1+1)*4.


16
Bu çok yardımcı. Ancak Haskell terminolojisinin insanların “zayıf normal kafa formu”, “katı” vb. Terimlerle anlamasını daha karmaşık hale getirip getirmediğini merak edemiyorum. Seni doğru anlarsam, kulağa hoş geliyor! işleci, daha sonra değerlendirmek üzere anonim bir bloğu saklamak yerine bir ifadenin değerlendirilen değerini saklamak anlamına gelir. Bu makul bir yorum mu yoksa daha fazlası mı var?
David

71
@David Soru, değerin ne kadar değerlendirileceği. Zayıf kafalı normal form şu anlama gelir: en dıştaki kurucuya ulaşana kadar değerlendir. Normal form, değerlendirilmemiş bileşenler kalmayana kadar tüm değeri değerlendirmek anlamına gelir. Haskell her türlü değerlendirme derinliğine izin verdiğinden, bunu tanımlamak için zengin bir terminolojiye sahiptir. Bu ayrımları yalnızca değere göre çağrı semantiğini destekleyen dillerde bulamazsınız.
Don Stewart

8
@David: Burada daha ayrıntılı bir açıklama yazdım: Haskell: Zayıf Kafa Normal Formu nedir? . Patlama desenlerinden veya katılık ek açıklamalarından bahsetmeme rağmen, bunlar kullanmaya eşdeğerdir seq.
hammar

1
Sadece anladığımdan emin olmak için: bu tembellik sadece derleme zamanı argümanlarında bilinmeyenle mi ilgili? Yani, kaynak kodun gerçekten bir ifadesi (2 + 2) varsa , bilinen sayıları eklemek için bir koda sahip olmak yerine yine de 4'e optimize edilir mi?
Melek

1
Merhaba Angel, derleyicinin ne kadar optimize etmek istediğine bağlı. Bazı şeyleri zayıf normal kafa formuna zorlamak istediğimizi söylemek için patlama kullanmamıza rağmen, "lütfen bu thunk'ı henüz değerlendirmediğinizden emin olun (ve ne gerek ne de istemiyoruz) ileri adım at. " Anlamsal bir bakış açısıyla, "n = 2 + 2" ifadesi göz önüne alındığında , n ile ilgili herhangi bir referansın şimdi değerlendirilip değerlendirilmediğini veya önceden değerlendirilip değerlendirilmediğini bile söyleyemezsiniz .
cjs

92

Sıkı ve katı olmayan yapıcı argümanları arasındaki farkı görmenin basit bir yolu, tanımsız olduklarında nasıl davrandıklarıdır. verilmiş

data Foo = Foo Int !Int

first (Foo x _) = x
second (Foo _ y) = y

Katı olmayan argüman tarafından değerlendirilmediğinden second, içeri aktarma undefinedbir soruna neden olmaz:

> second (Foo undefined 1)
1

Ancak undefineddeğeri kullanmasak bile katı argüman olamaz :

> first (Foo 1 undefined)
*** Exception: Prelude.undefined

2
Bu Curt Sampson'ın cevabından daha iyidir, çünkü aslında !iç uygulama ayrıntılarını incelemek yerine sembolün sahip olduğu kullanıcı tarafından gözlemlenebilir etkileri açıklar .
David Grayson

26

Bunun kesin bir ek açıklama olduğuna inanıyorum.

Haskell saf ve tembel bir işlevsel dildir, ancak bazen tembellik yükü çok fazla veya savurgan olabilir. Böylece bununla başa çıkmak için derleyiciden etraftaki thunkları ayrıştırmak yerine bir işlevin argümanlarını tam olarak değerlendirmesini isteyebilirsiniz.

Bu sayfada daha fazla bilgi var: Performans / Sıkılık .


Hmm, referans verdiğiniz sayfadaki örnek! bir işlevi çağırırken. Ama bu bir tür bildirime koymaktan farklı görünüyor. Neyi kaçırıyorum?
David

Türün bir örneğini oluşturmak da bir ifadedir. Tür yapıcılarını, sağlanan bağımsız değişkenler göz önüne alındığında, belirtilen türün yeni örneklerini döndüren işlevler olarak düşünebilirsiniz.
Chris Vest

4
Aslında, tüm niyet ve amaçlar için, tip tanımına olan işlevler; bunları kısmen uygulayabilir, diğer işlevlere geçirebilirsiniz (örneğin, map Just [1,2,3][Sadece 1, Sadece 2, Sadece 3] almak için) vb. Tamamen ilgisiz bir tesisin yanı sıra onlarla eşleşmeyi modelleme yeteneğini düşünmeyi yararlı buluyorum.
cjs
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.