Yan etkileri ele almak için IO monad modelinin yararı tamamen akademik midir?


17

Başka bir FP + yan etki sorusu için özür dilerim, ama benim için buna cevap veren mevcut bir tane bulamadım.

İşlevsel programlama (sınırlı) anlayışım, durum / yan etkilerin en aza indirilmesi ve vatansız mantıktan ayrı tutulması gerektiğidir.

Ayrıca Haskell'in bu yaklaşımı bir araya getiriyorum, IO monad, bunu, programın kapsamı dışında düşünülen, daha sonraki yürütme için bir kapta durumsal eylemler sararak başarıyor.

Bu kalıbı anlamaya çalışıyorum, ama aslında bir Python projesinde kullanıp kullanmayacağınızı belirlemek için, bu yüzden eğer Haskell özelliklerini önlemek istiyorum.

Ham örnek geliyor.

Programım bir XML dosyasını JSON dosyasına dönüştürürse:

def main():
    xml_data = read_file('input.xml')  # impure
    json_data = convert(xml_data)  # pure
    write_file('output.json', json_data) # impure

IO monad'ın yaklaşımı bunu etkin bir şekilde yapmak değil mi:

steps = list(
    read_file,
    convert,
    write_file,
)

o zaman bu adımları gerçekten çağırmakla değil , tercümanın yapmasına izin vererek kendini sorumluluktan kurtarsın mı?

Ya da başka bir deyişle, yazmak gibi:

def main():  # pure
    def inner():  # impure
        xml_data = read_file('input.xml')
        json_data = convert(xml_data)
        write_file('output.json', json_data)
    return inner

daha sonra başka birisinin aramasını beklemek inner()ve işinizin bittiğini söylemek main()saf.

Tüm program temel olarak IO monadında yer alacak.

Kod gerçekten yürütüldüğünde , dosyayı okuduktan sonra her şey o dosyanın durumuna bağlıdır, bu yüzden zorunlu uygulama ile aynı durumla ilgili hatalardan muzdarip olacak, bu yüzden bunu koruyacak bir programcı olarak gerçekten bir şey kazandınız mı?

Durumsal davranışı azaltmanın ve izole etmenin yararını takdir ediyorum , bu yüzden zorunlu sürümü bu şekilde yapılandırdım: girdileri toplamak, saf şeyler yapmak, çıktıları tükürmek. Umarım convert()tamamen saf olabilir ve saklanabilirlik, iplik güvenliği vb.

Monadik tiplerin, özellikle karşılaştırılabilir tiplerde çalışan boru hatlarında yararlı olabileceğini de takdir ediyorum, ancak zaten böyle bir boru hattında olmadığı sürece IO'nun neden monad kullanması gerektiğini anlamıyorum.

IO monad modelinin getirdiği yan etkilerle uğraşmanın ek bir yararı var mı?


1
Bu videoyu izlemelisin . Monadların harikaları nihayet Kategori Teorisi veya Haskell'e başvurmadan ortaya çıkar. Monad'ların önemsiz bir şekilde JavaScript'te ifade edildiği ve Ajax'ın ana etkinleştiricilerinden biri olduğu ortaya çıkıyor. Monadlar inanılmaz. Bunlar, karmaşıklığı yönetmek için muazzam bir güce sahip, neredeyse önemsiz bir şekilde uygulanan basit şeylerdir. Ancak onları anlamak şaşırtıcı derecede zordur ve çoğu insan, o ah-ha anına sahip olduklarında, onları başkalarına açıklama yeteneğini kaybediyor gibi görünmektedir.
Robert Harvey

İyi video, teşekkürler. Aslında bir JS intro fonksiyonel programlama bu şeyler hakkında öğrendim (sonra bir milyon daha okuyun…). Bunu izlemesine rağmen, sorumun Crock'un bu videoda ele almadığı IO monad'a özgü olduğundan eminim.
Stu Cox

Hmm ... AJAX bir G / Ç biçimi olarak kabul edilmiyor mu?
Robert Harvey

1
mainBir Haskell programındaki türün IO ()bir IO eylemi olduğuna dikkat edin . Bu aslında bir işlev değil; bu bir değer . Tüm programınız, dil çalışma zamanına ne yapması gerektiğini söyleyen talimatları içeren saf bir değerdir. Tüm saf olmayan şeyler (aslında ES eylemlerini gerçekleştirme) programınızın kapsamı dışındadır.
Wyzard --Stop Zarar Monica--

Örneğin, monadik kısım bir hesaplamanın ( read_file) sonucunu alıp bir sonrakine ( write_file) argüman olarak kullanmanızdır . Sadece bir dizi bağımsız eyleminiz olsaydı, bir Monad'a ihtiyacınız olmazdı.
lortabac

Yanıtlar:


14

Tüm program temel olarak IO monadında yer alacak.

Haskerlerin bakış açısından görmediğinizi düşündüğüm nokta bu. Yani böyle bir programımız var:

module Main

main :: IO ()
main = do
  xmlData <- readFile "input.xml"
  let jsonData = convert xmlData
  writeFile "output.json" jsonData

convert :: String -> String
convert xml = ...

Bence tipik bir Haskeller bunu ele alacak convert , saf kısım:

  1. Muhtemelen bu programın büyük bir kısmı ve çok daha karmaşık IO parçalardan ;
  2. Başa çıkmak zorunda kalmadan akıl yürütülebilir ve test edilebilir IO .

Yani bunu convert"içerilmiş" IOolarak değil, tecrit edildiği gibi görüyorlar IO. Türünden, her neyseconvert yaparsanız yapın asla bir IOeylemde olan hiçbir şeye bağlı olamaz .

Kod gerçekten yürütüldüğünde, dosyayı okuduktan sonra her şey o dosyanın durumuna bağlıdır, bu yüzden zorunlu uygulama ile aynı durumla ilgili hatalardan muzdarip olacak, bu yüzden bunu koruyacak bir programcı olarak gerçekten bir şey kazandınız mı?

Bunun iki şeye ayrıldığını söyleyebilirim:

  1. Program, değerini çalıştırdığında argüman içinconvert dosyanın durumuna bağlıdır.
  2. Ancak convertişlevin yaptığı , dosyanın durumuna bağlı değildir. farklı noktalarda farklı argümanlarla çağrılsa bile converther zaman aynı işlevdir .

Bu biraz soyut bir nokta, ama Haskellers'ın bu konuda konuşurken ne anlama geldiğinin gerçekten anahtarı. Herhangi bir geçerli argüman convertverildiğinde , bu argüman için doğru bir sonuç üretecek şekilde yazmak istiyorsunuz . Buna baktığınızda, dosya okumanın durum bilgisi olan bir işlem olması, denkleme girmez; önemli olan, hangi argümanın beslendiği ve nereden gelse gelsin, onu doğru bir şekilde ele alması gerektiğidir. Ve saflığın girdisiyle yapabileceklerini kısıtlaması , bu muhakemeyi basitleştirir.convertconvert

Dolayısıyla convert, bazı argümanlardan yanlış sonuçlar üretir ve readFilebunu böyle bir argümanla beslerse, bunu devlet tarafından getirilen bir hata olarak görmüyoruz . Saf bir fonksiyonda bir hata!


Sanırım bu en iyi açıklama (diğerleri de benim için bir şeyler açıklığa yardımcı olmasına rağmen), teşekkürler.
Stu Cox

Python'da monad kullanmanın sadece bir (statik) tipine sahip olduğundan daha az yararı olabileceğini ve bu nedenle herhangi bir şey için herhangi bir garanti vermeyeceğini belirtmek gerekir mi?
jk.

7

"Tamamen akademik" derken ne demek istediğinizden emin olmak zor, ama bence cevap çoğunlukla "hayır".

Simon Peyton Jones'un ( şiddetle okunması önerilir!) Garip Kadroyla Mücadelede açıklandığı gibi , monadik I / O, Haskell'in I / O ile başa çıkma şeklindeki gerçek problemleri çözmeyi amaçlıyordu. Burada kopyalamayacağım İstekler ve Yanıtlar ile sunucu örneğini okuyun; çok öğretici.

Haskell, Python'dan farklı olarak, kendi tür sistemi tarafından uygulanabilecek bir "saf" hesaplama tarzını teşvik eder. Tabii ki, bu stile uymak için Python'da programlama yaparken öz disiplini kullanabilirsiniz, ancak yazmadığınız modüller ne olacak? Tür sisteminden (ve ortak kütüphanelerden) fazla yardım almadan, monadik I / O muhtemelen Python'da daha az yararlıdır. Dil felsefesi sadece saf / saf olmayan bir ayrımı zorunlu kılmak anlamına gelmez.

Bunun Haskell ve Python'un farklı felsefeleri hakkında akademik monadik I / O'nun ne olduğu hakkında daha fazla şey söylediğini unutmayın. Python için kullanmam.

Diğer bir şey. Diyorsun:

Tüm program temel olarak IO monadında yer alacak.

Haskell mainişlevinin "yaşadığı" doğrudur IO, ancak gerçek Haskell programları IOihtiyaç duyulmadığında kullanılmaması önerilir . G / Ç yapması gerekmeyen neredeyse yazdığınız her işlevin türü olmamalıdırIO .

Bu yüzden son örneğinizde geriye doğru aldığını söyleyebilirim: mainsaf değil (çünkü dosyaları okur ve yazar) ama temel işlevler convertsaftır.


3

IO neden saf değildir? Çünkü farklı zamanlarda farklı değerler döndürebilir. Şu ya da bu şekilde hesaba katılması gereken zamana bağımlılık var . Bu tembel değerlendirme ile daha da önemlidir. Aşağıdaki programı düşünün:

main = do  
    putStrLn "Please enter your name"  
    name <- getLine
    putStrLn $ "Hello, " ++ name

Bir IO monad olmasaydı, ilk istem neden çıktı alır? Buna bağlı hiçbir şey yoktur, bu nedenle tembel değerlendirme asla talep edilmeyeceği anlamına gelir. Ayrıca, giriş okunmadan önce istemin çıkmasını zorlayan hiçbir şey yoktur. Bilgisayar söz konusu olduğunda, bir IO monad olmadan, bu ilk iki ifade birbirinden tamamen bağımsızdır. Neyse ki, nameikinci ikisine bir emir verir.

Sipariş bağımlılığı sorununu çözmenin başka yolları da vardır, ancak bir IO monad kullanmak, zorunlu kodun küçük bölümleri olmadan, her şeyin tembel fonksiyonel alanda kalmasına izin vermenin en basit yoludur (en azından dil açısından). Aynı zamanda en esnektir. Örneğin, kullanıcı girişini temel alarak çalışma zamanında bir GÇ boru hattını dinamik olarak kolayca oluşturabilirsiniz.


2

İşlevsel programlama (sınırlı) anlayışım, durum / yan etkilerin en aza indirilmesi ve vatansız mantıktan ayrı tutulması gerektiğidir.

Bu sadece işlevsel programlama değil; bu genellikle her dilde iyi bir fikirdir. Eğer birim testi yaparsanız, yolu ayrı bölünmüş read_file(), convert()ve write_file(), rağmen çünkü doğal olarak mükemmel geliyor convert()bunun için testler yazma, kod kadar en karmaşık ve en büyük kısmı tarafından olmak nispeten kolaydır: Eğer kurmak için gereken tüm giriş parametresidir . Fonksiyonu çağırmadan önce ve sonra dosya sisteminde bir şeyler oluşturmanız ve / veya okumanız gerektiğinden (fonksiyonların kendileri neredeyse önemsiz olsa bile) read_file()ve testleri yazmak write_file()biraz daha zordur. İdeal olarak, bu tür fonksiyonları o kadar basit hale getirirsiniz ki, onları test etmemekte rahat hissedersiniz ve böylece kendinizi çok fazla güçlükten kurtarırsınız.

Burada Python ve Haskell arasındaki fark Haskell'in fonksiyonların hiçbir yan etkisi olmadığını ispatlayabilen bir tip kontrol cihazına sahip olmasıdır. Python'da hiç kimsenin yanlışlıkla bir dosya okuma veya yazma işlevine convert()(diyelim ki read_config_file()) içine düşmemesini ummanız gerekir . Haskell'de convert :: String -> String, herhangi bir IOmonad olmadan, beyan ettiğiniz veya benzer bir şey yaptığınızda , tür denetleyicisi, bunun yalnızca giriş parametresine ve başka bir şeye dayanmayan saf bir işlev olduğunu garanti eder. Birisi convertbir yapılandırma dosyasını okumak için değiştirmeye çalışırsa , derhal fonksiyonun saflığını bozduğunu gösteren derleyici hatalarını görecektir. (Ve umarım onlar taşımak için mantıklı yeterince olurdu read_config_filedışına convertve içine geçmesi sonucunu convertsaflığını koruyarak.)

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.