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.
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 .