Haskell'in kesinlik noktaları nelerdir?


90

Haskell'in varsayılan olarak tembel olduğunu hepimiz biliyoruz (veya bilmeliyiz). Değerlendirilmesi gerekmedikçe hiçbir şey değerlendirilmez. Peki bir şey ne zaman değerlendirilmeli? Haskell'in katı olması gereken noktalar var. Ben bunlara "kesinlik noktaları" diyorum, ancak bu özel terim düşündüğüm kadar yaygın değil. Bana göre:

Haskell'de azaltma (veya değerlendirme) yalnızca kesinlik noktalarında gerçekleşir.

Soru yani: ne tam olarak , Haskell'ın katılık noktalarıdır? Benim sezgim main, seq/ patlama kalıpları, örüntü eşleştirme ve IOgerçekleştirilen herhangi bir eylemin mainbirincil katılık noktaları olduğunu söylüyor, ancak bunu neden bildiğimi gerçekten bilmiyorum.

(Ayrıca, "kesinlik noktaları" olarak adlandırılmazlarsa , bunlara ne denir?)

İyi bir yanıtın WHNF hakkında bazı tartışmaları içereceğini tahmin ediyorum. Ayrıca lambda hesabına da değebileceğini düşünüyorum.


Düzenleme: bu soru hakkında ek düşünceler.

Bu soru üzerine düşündüğüm gibi, katılık noktası tanımına bir şeyler eklemenin daha açık olacağını düşünüyorum. Katılık noktalarının farklı bağlamları ve değişen derinlikleri (veya katılıkları) olabilir. "Haskell'de indirgeme yalnızca katılık noktalarında gerçekleşir" tanımıma geri dönersek, bu tanıma şu cümleyi ekleyelim: "bir katılık noktası yalnızca çevreleyen bağlam değerlendirildiğinde veya azaltıldığında tetiklenir."

Öyleyse, istediğim cevaba başlamanıza izin verin. mainbir katılık noktasıdır. İçeriğinin birincil katılık noktası olarak özel olarak belirlenmiştir: program. Program ( mainbağlamı) değerlendirildiğinde, main'in katılık noktası etkinleştirilir. Main'in derinliği maksimumdur: tam olarak değerlendirilmelidir. Ana genellikle, bağlamı şu olan katılık noktaları olan IO eylemlerinden oluşur.main .

Şimdi deneyin: seqbu terimlerle örnek eşleştirmeyi tartışın ve deneyin . İşlev uygulamasının nüanslarını açıklayın: ne kadar katıdır? Nasıl değil Peki ya deepseq? letve caseifadeler? unsafePerformIO? Debug.Trace? Üst düzey tanımlar? Katı veri türleri? Patlama kalıpları? Vb. Bu öğelerden kaç tanesi yalnızca sıralama veya örüntü eşleştirme açısından tanımlanabilir?


10
Sezgisel listeniz muhtemelen çok ortogonal değildir. Bunun seqve örüntü eşleştirmesinin yeterli olduğundan şüpheleniyorum , gerisi bunlara göre tanımlandı. IOÖrneğin, desen eşleştirmenin eylemlerin omurga sertliğini garanti ettiğini düşünüyorum .
CA McCann

Yerleşik +sayısal türlerde olduğu gibi temel öğeler de katılığı zorlar ve aynısının saf FFI çağrıları için de geçerli olduğunu varsayıyorum.
hammar

4
Burada karıştırılan iki kavram var gibi görünüyor. Desen eşleştirme ve sıra ve çarpma örüntüleri, bir ifadenin alt ifadelerinde katı hale gelme yollarıdır - yani, üst ifade değerlendirilirse, alt ifade de öyle. Öte yandan, ana performans gösteren IO eylemleri, değerlendirmenin nasıl başladığıdır . Bunlar farklı şeylerdir ve bunları aynı listeye dahil etmek bir tür tip hatasıdır.
Chris Smith

@ChrisSmith Bu iki farklı durumu birbirine karıştırmaya çalışmıyorum; nasıl etkileşimde bulunduklarına dair daha fazla açıklama istiyorum. Kesinlik bir şekilde olur ve her iki durum da önemlidir, ancak farklı olsa da, katılığın "gerçekleştiği" kısımlar. (ve @ monadic: ಠ_ಠ)
Dan Burton

Bu sorunun yönlerini tam bir cevaba girmeden tartışmak istiyorsanız / buna ihtiyacınız varsa, bu soru için / r / haskell yazımdaki
Dan Burton

Yanıtlar:


46

Başlamak için iyi bir yer, bu makaleyi anlamaktır: Tembel Değerlendirme İçin Doğal Bir Anlambilim Assessution (Launchbury). Bu, ifadelerin GHC Çekirdeğine benzer küçük bir dil için ne zaman değerlendirildiğini size söyleyecektir. Geriye kalan soru, Haskell'in Core ile nasıl eşleştirileceğidir ve bu çevirinin çoğu Haskell raporunda verilmiştir. GHC'de bu sürece "şekeri temizleme" diyoruz, çünkü sözdizimsel şekeri ortadan kaldırıyor.

Pekala, hikayenin tamamı bu değil, çünkü GHC, tasarlama ve kod oluşturma arasında bir dizi optimizasyon içeriyor ve bu dönüşümlerin çoğu Çekirdeği yeniden düzenleyecek, böylece şeyler farklı zamanlarda değerlendirilecek (özellikle katılık analizi, şeylerin değerlendirilmesine neden olacaktır. daha erken). Dolayısıyla , programınızın nasıl değerlendirileceğini gerçekten anlamak için , GHC tarafından üretilen Çekirdeğe bakmanız gerekir.

Belki bu cevap size biraz soyut gelebilir (özellikle patlama kalıplarından veya sırasından bahsetmedim), ancak kesin bir şey istediniz ve bu, yapabileceğimizin en iyisi ile ilgili.


18
GHC'nin "şeker çözme" dediği şeyde, kaldırılan sözdizimsel şekerin Haskell dilinin kendisinin gerçek sözdizimini içermesini her zaman eğlenceli bulmuşumdur ... GHC'nin gerçekte GHC için optimize edici bir derleyici olduğunu ima edebilir. Bu arada, Haskell'i Core'a çevirmek için çok ayrıntılı bir ön uç da içeren temel dil . :]
CA McCann

Yazı sistemleri tam olarak işlenmiyor ... özellikle de hatırladığım kadarıyla sadece tip sınıflarını açık sözlüklere çevirmekle ilgili değil. Ve anladığım kadarıyla en son TF / GADT öğeleri, bu boşluğu daha da genişletti.
sclv


20

Muhtemelen bu soruyu , Haskell hangi koşullar altında bir ifadeyi değerlendirecektir? (Belki de "zayıf kafa normal formu" nu yapıştırabilirsiniz.)

İlk yaklaşım olarak, bunu şu şekilde belirtebiliriz:

  • IO eylemlerini yürütmek, "ihtiyaç duydukları" tüm ifadeleri değerlendirecektir. (Bu nedenle, IO eyleminin yürütüldüğünü bilmeniz gerekir, örneğin adı main mi, yoksa main'den mi çağrıldı VE eylemin neye ihtiyacı olduğunu bilmeniz gerekir.)
  • Değerlendirilmekte olan bir ifade (hey, bu yinelemeli bir tanımdır!) İhtiyaç duyduğu tüm ifadeleri değerlendirecektir.

Sezgisel listenizden, ana ve IO eylemleri ilk kategoriye, sıra ve şablon eşleştirme ikinci kategoriye girer. Ama bence ilk kategori sizin "kesinlik noktası" fikrinizle daha uyumludur, çünkü aslında Haskell'deki değerlendirmeyi gözlemlenebilir hale getiriyoruz. kullanıcılar için etkiler .

Haskell geniş bir dil olduğundan, tüm ayrıntıları özel olarak vermek büyük bir görevdir. Aynı zamanda oldukça ince, çünkü Concurrent Haskell olayları spekülatif olarak değerlendirebilir, sonunda sonucu kullanmasak bile: bu, değerlendirmeye neden olan üçüncü tür şeylerdir. İkinci kategori oldukça iyi incelenmiştir: ilgili fonksiyonların katılığına bakmak istiyorsunuz . İlk kategorinin de bir tür "katılık" olduğu düşünülebilir, ancak bu biraz tehlikelidir çünkü evaluate xve seq x $ return ()aslında farklı şeylerdir! IO monadına bir tür anlambilim verirseniz (açıkça bir RealWorld#belirteci iletmek basit durumlar için işe yarar), ancak genel olarak bu tür tabakalı katılık analizinin bir adı olup olmadığını bilmiyorum.


17

C, bir işlenenin diğerinden önce değerlendirileceği belirli işlemler için garanti olan sıra noktaları kavramına sahiptir . Bence bu mevcut en yakın kavram, ancak esasen eşdeğer olan katılık noktası (veya muhtemelen kuvvet noktası ) terimi Haskell düşüncesine daha çok uygun.

Pratikte Haskell tamamen tembel bir dil değildir: örneğin kalıp eşleştirme genellikle katıdır (Bu nedenle, bir kalıp eşleştirmeyi denemek, değerlendirmeyi en azından eşleşmeyi kabul edecek veya reddedecek kadar uzağa zorlar.

Programcılar seq, sonucun hiç kullanılıp kullanılmayacağına bakılmaksızın bir ifadeyi değerlendirmeye zorlamak için de ilkeli kullanabilir .

$!açısından tanımlanmıştır seq.

- Tembel ve katı olmayan .

Dolayısıyla, !/ $!ve hakkındaki düşünceniz seqaslında doğrudur, ancak kalıp eşleştirme daha ince kurallara tabidir. ~Elbette tembel desen eşleştirmeye zorlamak için her zaman kullanabilirsiniz . Aynı makaleden ilginç bir nokta:

Kesinlik çözümleyicisi ayrıca, alt ifadelerin her zaman dış ifade tarafından gerekli olduğu durumları da arar ve bunları istekli değerlendirmeye dönüştürür. Bunu yapabilir, çünkü semantik ("alt" açısından) değişmez.

Tavşan deliğinden devam edelim ve GHC tarafından gerçekleştirilen optimizasyonlar için belgelere bakalım:

Kesinlik analizi, GHC'nin derleme sırasında hangi verilere kesinlikle 'her zaman ihtiyaç duyulacağını' belirlemeye çalıştığı bir süreçtir. GHC daha sonra, hesaplamayı depolamak ve daha sonra yürütmek için normal (daha yüksek ek yük) süreç yerine, yalnızca bu tür verileri hesaplamak için kod oluşturabilir.

- GHC Optimizasyonları: Sıkılık Analizi .

Diğer bir deyişle, kesin kod, optimizasyon olarak herhangi bir yerde üretilebilir , çünkü verilere her zaman ihtiyaç duyulduğunda (ve / veya yalnızca bir kez kullanılabilir) thunk'ların oluşturulması gereksiz yere pahalıdır.

… Değer üzerinde daha fazla değerlendirme yapılamaz; normal formda olduğu söyleniyor . Bir değer üzerinde en azından biraz değerlendirme yapmak için ara adımlardan herhangi birinde isek, bu zayıf kafa normal formundadır (WHNF). (Ayrıca bir 'kafa normal formu' vardır, ancak Haskell'de kullanılmaz.) WHNF'de bir şeyi tam olarak değerlendirmek, onu normal formdaki bir şeye indirger ...

- Vikikitap Haskell: Tembellik

( 1. baş pozisyonunda beta-redeks yoksa bir terim kafa normal formundadır . Redex, redex olmayanların lambda abstraktörlerinden önce geliyorsa baş redeksidir 2. ) Yani bir thunk'u zorlamaya başladığınızda, WHNF'de çalışıyorsunuz; Zorlanacak daha fazla şey kalmadığında normal formdasın. Bir başka ilginç nokta:

… Bir noktada kullanıcıya yazdırmamız gerekirse, bunu tam olarak değerlendirmemiz gerekir…

Doğal olarak ima Hangi, gerçekten, herhangi bir IOeylem gerçekleştirilebilir main yapar Haskell programları yapmak, aslında, şeyler düşünüyor açık olmalı kuvvet değerlendirme,. Tanımlanan sıralamadan geçmesi gereken her şey mainnormal biçimde olmalıdır ve bu nedenle katı bir değerlendirmeye tabidir.

CA McCann, sağ yorumların içinde var gerçi: ilgili özel olan tek şey mainyani mainözel olarak tanımlanır; yapıcı üzerinde desen eşleştirme, IOmonad tarafından empoze edilen diziyi sağlamak için yeterlidir . Bu bakımdan sadece seqve model eşleştirme esastır.


4
Aslında "bir noktada kullanıcıya yazdırmamız gerekirse, onu tam olarak değerlendirmemiz gerekirdi" sözü tamamen doğru değildir. ShowYazdırılan değerin örneği kadar katıdır .
nominolo

10

Haskell, AFAIK saf tembel bir dil değil, katı olmayan bir dildir. Bu, şartları mümkün olan son anda mutlaka değerlendirmediği anlamına gelir.

Haskell'in "tembellik" modeli için iyi bir kaynak burada bulunabilir: http://en.wikibooks.org/wiki/Haskell/Laziness

Temel olarak, bir thunk ve zayıf başlık normal form WHNF arasındaki farkı anlamak önemlidir.

Anladığım kadarıyla haskell, zorunlu dillere kıyasla hesaplamaları geriye doğru çekiyor. Bunun anlamı şudur: "sekans" ve patlama modellerinin yokluğunda, sonuçta bir thunk değerlendirmesini zorlayan bir tür yan etki olacaktır, bu da daha önceki değerlendirmelere (gerçek tembellik) neden olabilir.

Bu korkunç bir alan sızıntısına yol açacağından, derleyici daha sonra yerden tasarruf etmek için thunks'ı nasıl ve ne zaman değerlendireceğini anlar. Programcı daha sonra iç içe geçmiş thunks biçiminde alan kullanımını daha da azaltmak için katılık ek açıklamaları (en.wikibooks.org/wiki/Haskell/Strictness, www.haskell.org/haskellwiki/Performance/Strictness) vererek bu süreci destekleyebilir.

Haskell'in operasyonel semantiği konusunda uzman değilim, bu yüzden bağlantıyı bir kaynak olarak bırakacağım.

Biraz daha kaynak:

http://www.haskell.org/haskellwiki/Performance/Laziness

http://www.haskell.org/haskellwiki/Haskell/Lazy_Evaluation


6

Tembel hiçbir şey yapmamak anlamına gelmez. Program modeliniz bir caseifadeyle eşleştiğinde , bir şeyi değerlendirir - zaten yeterli. Aksi takdirde hangi RHS'nin kullanılacağını anlayamaz. Kodunuzda herhangi bir durum ifadesi görmüyor musunuz? Endişelenmeyin, derleyici kodunuzu kullanmaktan kaçınmanın zor olduğu soyulmuş bir Haskell biçimine çeviriyor.

Yeni başlayanlar için temel kural lettembeldir, casedaha az tembeldir.


2
Ederken o Not casehep GHC Core değerlendirmeyi zorlar, düzenli Haskell bu yapmaz. Örneğin, deneyin case undefined of _ -> 42.
hammar

2
caseGHC'de Core, WHNF'ye caseyönelik argümanını değerlendirirken, Haskell'de uygun dalı seçmek için argümanını gerektiği kadar değerlendirir . Hammar'ın örneğinde, bu hiç de değil, ama içinde case 1:undefined of x:y:z -> 42, WHNF'den daha derin değerlendiriyor.
Maksimum

Ve ayrıca case something of (y,x) -> (x,y)değerlendirmeye de gerek yok something. Bu, tüm ürün türleri için geçerlidir.
Ingo

@Ingo - bu yanlış. somethingtuple yapıcısına ulaşmak için WHNF'ye değerlendirilmesi gerekir.
John L

John - Neden? Bunun bir demet olması gerektiğini biliyoruz, öyleyse onu değerlendirmenin amacı nerede? X ve y, demeti değerlendiren ve uygun yuvayı çıkaran koda bağlıysa, kendilerine ihtiyaç duyulursa yeterlidir.
Ingo

4

Bu, karmayı hedefleyen tam bir cevap değil, sadece bulmacanın bir parçasıdır - bu anlambilimle ilgili olduğu ölçüde, aynı anlambilimini . Buradaki iyi bir örnek - ve proje aynı zamanda Haskell semantiğini tipik olarak nasıl düşündüğümüze de değiniyor - aynı semantiği korurken değerlendirme stratejilerini kökten değiştiren Eager Haskell projesiydi : http://csg.csail.mit.edu/ pubs / haskell.html


2

Glasgow Haskell derleyicisi, kodunuzu çekirdek adı verilen Lambda-kalkülüs benzeri bir dile çevirir . Bu dilde, bir şeyi bir- caseifadeyle eşleştirdiğinizde bir şey değerlendirilecektir. Bu nedenle, bir işlev çağrılırsa, en dıştaki kurucu ve yalnızca o (zorunlu alanlar yoksa) değerlendirilecektir. Başka her şey bir gümbürtüyle saklanır. (Thunks, letbağlamalarla tanıtılmıştır ).

Tabii ki gerçek dilde olan tam olarak bu değil. Derleyici, Haskell'i çok sofistike bir şekilde Core'a dönüştürür, mümkün olduğu kadar çok şeyi tembel ve her zaman ihtiyaç duyulan her şeyi tembel hale getirir. Ek olarak, her zaman katı olan kutulanmamış değerler ve tuplelar vardır.

Bir işlevi elle değerlendirmeye çalışırsanız, temel olarak şunları düşünebilirsiniz:

  • Geri dönüşün en dıştaki yapıcısını değerlendirmeye çalışın.
  • Sonucu almak için başka bir şeye ihtiyaç duyulursa (ancak gerçekten gerekliyse) da değerlendirilecektir. Sıranın önemi yok.
  • IO durumunda, ilkinden sonuncusuna kadar tüm ifadelerin sonuçlarını değerlendirmelisiniz. IO monad, değerlendirmeyi belirli bir sıraya göre zorlamak için bazı hileler yaptığından, bu biraz daha karmaşıktır.

0

Haskell'in varsayılan olarak tembel olduğunu hepimiz biliyoruz (veya bilmeliyiz). Değerlendirilmesi gerekmedikçe hiçbir şey değerlendirilmez.

Hayır.

Haskell tembel bir dil değil

Haskell, değerlendirme sırasının önemli olmadığı bir dildir yan etkisi olmadığı için .

Değerlendirme sırasının önemli olmadığı tam olarak doğru değil, çünkü dil sonsuz döngülere izin veriyor. Dikkatli olmazsanız, farklı bir değerlendirme sırası sonlu bir zamanda sona erdirmeye yol açarken bir alt ifadeyi sonsuza kadar değerlendirdiğiniz bir çıkmaza takılıp kalmak mümkündür. Yani şunu söylemek daha doğru olur:

  • Haskell uygulamaları, programı sona erdiren herhangi bir değerlendirme emri varsa sona erecek şekilde değerlendirmelidir. Ancak olası her değerlendirme emri sonlandırılamazsa, uygulama sona erdirilemez.

Bu, uygulamaları, programı değerlendirme biçimlerinde hâlâ büyük bir özgürlükle karşı karşıya bırakır.

Bir Haskell programı tek bir ifadedir, yani let {tüm üst düzey bağlamalar} in Main.main . Değerlendirme, ifadeyi değiştiren (yürütülen programın mevcut durumunu temsil eden) bir indirgeme (küçük-) adımları dizisi olarak anlaşılabilir.

İndirgeme adımlarını iki kategoriye ayırabilirsiniz: kanıtlanabilir şekilde gerekli olanlar (kanıtlanabilir bir şekilde herhangi bir azaltma dizisinin bir parçası olacaktır) ve olmayanlar. Kanıtlanabilir şekilde gerekli indirgemeleri biraz belirsiz bir şekilde iki alt kategoriye ayırabilirsiniz: "açıkça" gerekli olanlar ve gerekli olduklarını kanıtlamak için bazı önemsiz analizler gerektiren olanlar.

Sadece açıkça gerekli olan azaltmaları gerçekleştirmek "tembel değerlendirme" olarak adlandırılan şeydir. Haskell'in tamamen tembel bir değerlendirme uygulamasının var olup olmadığını bilmiyorum. Sarılmalar olabilirdi. GHC kesinlikle değil.

GHC kanıtlanabilir şekilde gerekli olmayan azaltma adımlarını derleme zamanında gerçekleştirir; örneğin, yerini alacak 1+2::Intolan 3::Intsonucun kullanılacaktır ispat edemez bile.

GHC ayrıca bazı durumlarda çalışma zamanında kanıtlanamayacak şekilde gerekli indirimleri de gerçekleştirebilir. Örneğin, değerlendirmek için kod üretilirken f (x+y), tip olup olmadığı xve değerleri çalışma zamanında bilinecek, ancak argümanını kullandığı kanıtlanamayacak, çağırmadan önce hesaplama yapmamak için bir neden yoktur . Daha az yığın alanı ve daha az kod alanı kullanır ve bağımsız değişken kullanılmasa bile muhtemelen daha hızlıdır. Ancak, GHC'nin bu tür optimizasyon fırsatlarını gerçekten alıp almadığını bilmiyorum.yIntfx+yf

GHC, yalnızca oldukça karmaşık çapraz modül analizleri ile gerekli olduğu kanıtlanan değerlendirme adımlarını kesinlikle çalışma zamanında gerçekleştirir. Bu son derece yaygındır ve gerçekçi programların değerlendirmesinin büyük bir kısmını temsil edebilir. Tembel değerlendirme, son çare olan bir geri dönüş değerlendirme stratejisidir; kural olarak olan şey değil.

GHC'nin çalışma zamanında çok daha spekülatif değerlendirme yapan bir "iyimser değerlendirme" dalı vardı. İyi performans göstermediği için değil, karmaşıklığı ve devam eden bakım yükü nedeniyle terk edildi. Haskell, Python veya C ++ kadar popüler olsaydı, eminim çok daha karmaşık çalışma zamanı değerlendirme stratejilerine sahip, derin şirketler tarafından sürdürülen uygulamalar olurdu. Tembel olmayan değerlendirme, dilde bir değişiklik değil, sadece bir mühendislik sorunu.

Azaltma, üst düzey G / Ç ile sağlanır, başka hiçbir şey yoktur

Dış dünya ile etkileşimi, özel yan etkili indirgeme kuralları aracılığıyla modelleyebilirsiniz: "Mevcut program formdaysa getChar >>= <expr>, standart girdiden bir karakter alın ve programı <expr>, sahip olduğunuz karaktere uygulanacak şekilde azaltın ."

Çalışma süresi sisteminin tüm amacı, programı bu yan etkili formlardan birine sahip olana kadar değerlendirmek, ardından yan etkiyi yapmak, ardından programın sonlandırmayı ima eden bir biçime sahip olana kadar tekrar etmektir return ().

Neyin ne zaman azaltılacağına dair başka bir kural yoktur. Neyin neye indirgenebileceğine dair sadece kurallar vardır.

Örneğin, sadece kurallara ififadeler bu olarak if True then <expr1> else <expr2>indirgenebilir <expr1>, if False then <expr1> else <expr2>indirgenebilir <expr2>ve if <exc> then <expr1> else <expr2>burada, <exc>olağanüstü bir değer olup, istisnai bir değere düşürülebilir.

Programınızın şu anki durumunu temsil eden ifade bir ififade ise, o Trueveya Falseveya olana kadar durum üzerinde azaltma yapmaktan başka seçeneğiniz yoktur <exc>, çünkü ififadeden kurtulmanın ve ulaşma ümidinizin tek yolu budur . G / Ç kurallarından biriyle eşleşen bir durum. Ancak dil özelliği size bunu pek çok kelimeyle yapmanızı söylemiyor.

Bu tür örtük sıralama kısıtlamaları, değerlendirmenin gerçekleşmeye "zorlanmasının" tek yoludur. Bu, yeni başlayanlar için sık görülen bir kafa karışıklığı kaynağıdır. Örneğin, insanlar bazen foldlyazmak foldl (\x y -> x `seq` x+y)yerine yazarak daha katı olmaya çalışırlar foldl (+). Bu işe yaramaz ve hiçbir zaman işe yaramaz, çünkü hiçbir ifade kendini değerlendiremez. Değerlendirme yalnızca "yukarıdan gelebilir". seqbu konuda hiçbir şekilde özel değildir.

Azaltma her yerde olur

Haskell'de azaltma (veya değerlendirme) yalnızca kesinlik noktalarında gerçekleşir. [...] Benim sezgilerim, ana, sıra / patlama örüntüleri, örüntü eşleştirme ve ana yoluyla gerçekleştirilen herhangi bir GÇ eyleminin birincil kesinlik noktaları olduğunu söylüyor [...].

Bu ifadeyi nasıl anlamlandıracağımı bilmiyorum. Programın her bölümünün bir anlamı vardır ve bu anlam, indirgeme kuralları ile tanımlanır, bu nedenle indirgeme her yerde olur.

Bir işlev uygulamasını azaltmak için <expr1> <expr2>, veya <expr1>benzeri bir forma (\x -> <expr1'>)veya (getChar >>=)bir kuralla eşleşen başka bir şeye sahip olana kadar değerlendirmeniz gerekir . Ancak bazı nedenlerden ötürü işlev uygulaması, caseher zaman yaptığı halde "değerlendirmeyi zorladığı" iddia edilen ifadelerin listelerinde görünme eğiliminde değildir .

Bu yanlış kanı, başka bir cevapta bulunan Haskell wiki'den bir alıntıda görebilirsiniz:

Pratikte Haskell tamamen tembel bir dil değildir: örneğin kalıp eşleştirme genellikle katıdır

Bunu yazanlar için neyin "tamamen tembel bir dil" olarak nitelendirilebileceğini anlamıyorum, belki de çalışma zamanı hiçbir zaman hiçbir şey yapmadığı için her programın askıda kaldığı bir dil dışında. Kalıp eşleme, dilinizin bir özelliğiyse, bir noktada gerçekten yapmanız gerekir. Bunu yapmak için, araştırmacıyı modelle eşleşip eşleşmediğini belirlemeye yetecek kadar değerlendirmelisiniz. Prensipte mümkün olan bir kalıbı eşleştirmenin en tembel yolu budur.

~-öneksiz kalıplar programcılar tarafından genellikle "tembel" olarak adlandırılır, ancak dil özelliği onları "reddedilemez" olarak adlandırır. Onların tanımlayıcı özelliği, her zaman eşleşmeleridir. Her zaman eşleştikleri için, eşleşip eşleşmediklerini belirlemek için incelemeyi değerlendirmeniz gerekmez, bu nedenle tembel bir uygulama olmaz. Normal ve reddedilemez modeller arasındaki fark, hangi değerlendirme stratejisini kullanmanız gerektiği değil, hangi ifadelerle eşleştikleridir. Spesifikasyon, değerlendirme stratejileri hakkında hiçbir şey söylemiyor.


mainbir katılık noktasıdır. İçeriğinin birincil katılık noktası olarak özel olarak belirlenmiştir: program. Program ( mainbağlamı) değerlendirildiğinde, main'in katılık noktası etkinleştirilir. [...] Ana, genellikle bağlamı olan katılık noktaları olan IO eylemlerinden oluşur main.

Bunların herhangi birinin bir anlamı olduğuna ikna olmadım.

Main'in derinliği maksimumdur: tam olarak değerlendirilmelidir.

Hayır, mainG / Ç eylemlerinin en üst düzeyde görünmesini sağlamak için yalnızca "yüzeysel" olarak değerlendirilmesi gerekir. maintüm programdır ve program her çalıştırmada tamamen değerlendirilmez çünkü kodun tamamı her çalıştırma ile ilgili değildir (genel olarak).

seqBu terimlerle örnek eşleştirmeyi tartışın .

Desen eşleştirmeden zaten bahsetmiştim. seqbenzer kurallara göre tanımlanabilir case, örneğin: ve uygulama (\x -> <expr1>) `seq` <expr2>azaltır <expr2>. Bu, aynı caseve uygulamanın yaptığı gibi "değerlendirmeyi zorlar" . WHNF, bu ifadelerin "değerlendirmeye zorlama" nın ne anlama geldiğinin bir adıdır.

İşlev uygulamasının nüanslarını açıklayın: ne kadar katıdır? Nasıl değil

Sol ifadesinde tıpkı incelemesinde olduğu gibi casekatı. Ayrıca case, değiştirmeden sonra seçilen alternatifin RHS'sinde olduğu gibi , değiştirmeden sonra işlev gövdesinde de katıdır .

Peki ya deepseq?

Bu sadece bir kütüphane işlevi, yerleşik değil.

Bu arada, deepseqanlamsal olarak tuhaf. Yalnızca bir argüman almalıdır. Bence onu kim icat ederse seq, neden seqiki argümana ihtiyaç duyduğunu anlamadan körü körüne kopyaladı . deepseqDeneyimli Haskell programcıları arasında bile Haskell değerlendirmesinin kötü anlaşılmasının yaygın olduğunun kanıtı olarak 'ın adını ve özelliklerini kabul ediyorum .

letve caseifadeler?

Hakkında konuştum case. let, şekerleme ve tür kontrolünden sonra, ağaç biçiminde rastgele bir ifade grafiği yazmanın bir yoludur. İşte bununla ilgili bir makale .

unsafePerformIO?

Bir dereceye kadar indirim kuralları ile tanımlanabilir. Örneğin, case unsafePerformIO <expr> of <alts>azaltır unsafePerformIO (<expr> >>= \x -> return (case x of <alts>))ve sadece en üst düzeyde, unsafePerformIO <expr>azaltır <expr>.

Bu herhangi bir hatırlatma yapmaz. unsafePerformIOKendini açıkça ezberlemek için her ifadeyi yeniden yazarak ve bir IORefyerde ilişkili s ... oluşturarak notu simüle etmeye çalışabilirsiniz . Ancak, GHC'nin hafızaya alma davranışını asla yeniden oluşturamazsınız, çünkü optimizasyon sürecinin öngörülemeyen ayrıntılarına bağlıdır ve hatta tip güvenli olmadığı için ( IORefGHC belgelerinde meşhur polimorfik örnekte gösterildiği gibi ).

Debug.Trace?

Debug.Trace.tracesadece basit bir paketleyicidir unsafePerformIO.

Üst düzey tanımlar?

Üst düzey değişken bağlamaları, iç içe yerleştirmelerle aynıdır let. data, class, importVe bu tamamen farklı bir top oyunu.

Katı veri türleri? Patlama kalıpları?

Sadece şeker seq.

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.