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::Int
olan 3::Int
sonucun 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ığı x
ve 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.y
Int
f
x+y
f
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 if
ifadeler 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 if
ifade ise, o True
veya False
veya olana kadar durum üzerinde azaltma yapmaktan başka seçeneğiniz yoktur <exc>
, çünkü if
ifadeden 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 foldl
yazmak 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". seq
bu 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ı, case
her 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.
main
bir katılık noktasıdır. İçeriğinin birincil katılık noktası olarak özel olarak belirlenmiştir: program. Program ( main
bağ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, main
G / Ç eylemlerinin en üst düzeyde görünmesini sağlamak için yalnızca "yüzeysel" olarak değerlendirilmesi gerekir. main
tü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).
seq
Bu terimlerle örnek eşleştirmeyi tartışın .
Desen eşleştirmeden zaten bahsetmiştim. seq
benzer kurallara göre tanımlanabilir case
, örneğin: ve uygulama (\x -> <expr1>) `seq` <expr2>
azaltır <expr2>
. Bu, aynı case
ve 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 case
katı. 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, deepseq
anlamsal olarak tuhaf. Yalnızca bir argüman almalıdır. Bence onu kim icat ederse seq
, neden seq
iki argümana ihtiyaç duyduğunu anlamadan körü körüne kopyaladı . deepseq
Deneyimli 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 .
let
ve case
ifadeler?
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. unsafePerformIO
Kendini açıkça ezberlemek için her ifadeyi yeniden yazarak ve bir IORef
yerde 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 ( IORef
GHC belgelerinde meşhur polimorfik örnekte gösterildiği gibi ).
Debug.Trace
?
Debug.Trace.trace
sadece 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
, import
Ve bu tamamen farklı bir top oyunu.
Katı veri türleri? Patlama kalıpları?
Sadece şeker seq
.
seq
ve ö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 .