IO monad teknik olarak yanlış mı?


12

Haskell wiki'de IO monadının koşullu kullanımına ilişkin aşağıdaki örnek vardır (buraya bakın) .

when :: Bool -> IO () -> IO ()
when condition action world =
    if condition
      then action world
      else ((), world)

Bu örnekte, her şeyin daha anlaşılır IO aolması RealWorld -> (a, RealWorld)için tanımının yapıldığını unutmayın .

Bu snippet, IO monadında şartlı olarak bir eylem yürütür. Şimdi, varsayarak conditionolduğunu False, aksiyon actionyapılmamalıdır. Tembel semantik kullanmak gerçekten durum böyle olurdu. Bununla birlikte, burada Haskell'in teknik olarak katı olmadığı konuşulmaktadır. Bu, derleyicinin örneğin önceden action worldfarklı bir iş parçacığında çalışmasına izin verildiği ve daha sonra buna ihtiyaç duymadığını fark ettiğinde bu hesaplamayı atmasına izin verildiği anlamına gelir . Bununla birlikte, bu noktaya kadar yan etkiler zaten olmuş olacak.

Şimdi, IO monadını, yan etkiler sadece tüm program bittiğinde yayılacak şekilde uygulayabilir ve tam olarak hangi yan etkilerin uygulanması gerektiğini biliyoruz. Ancak durum böyle değildir, çünkü Haskell'de açıkça ara yan etkileri olan sonsuz programlar yazmak mümkündür.

Bu, IO monadının teknik olarak yanlış olduğu veya bunun olmasını engelleyen başka bir şey olduğu anlamına mı geliyor?


Bilgisayar Bilimine Hoşgeldiniz ! Sorunuz burada konu dışı: Programlama sorularıyla değil bilgisayar bilimi sorularıyla ilgileniyoruz ( SSS bölümümüze bakın ). Sorunuz Yığın Taşması ile ilgili konu üzerinde olabilir .
dkaeae

2
Bence bu bir bilgisayar bilimi sorusudur, çünkü pratik bir programlama sorusuyla değil, Haskell'in teorik anlamıyla ilgilenir.
Lasse

4
Programlama dili teorisine çok aşina değilim, ancak bu sorunun burada konu olduğunu düşünüyorum. Burada 'yanlış'ın ne anlama geldiğini açıklığa kavuşturmak yardımcı olabilir. Sizce IO monadının sahip olmaması gereken özelliği var mı?
Ayrık kertenkele

1
Bu program iyi yazılmış değil. Aslında ne yazmak istediğinden emin değilim. Tanımı whenyazı tipidir, ancak beyan ettiğiniz tipe sahip değildir ve bu kodu ilginç kılan şeyleri göremiyorum.
Gilles 'SO- kötü olmayı bırak'

2
Bu program, doğrudan bağlantılı Haskell-wiki sayfasından kelimesi kelimesine alınmıştır. Gerçekten yazmaz. Bunun nedeni , ES'nin iç kısımlarını daha okunaklı hale getirmek için IO aolarak tanımlanan varsayım altında yazılmasıdır RealWorld -> (a, RealWorld).
Lasse

Yanıtlar:


12

Bu, IOmonad'ın önerilen bir "yorumu" dur. Bu "yorumu" ciddiye almak istiyorsanız, "RealWorld" ü ciddiye almanız gerekir. Olup olmadığı alakasız action worldspekülatif değerlendirildi alır ya da olmasın, actionherhangi bir yan etkiye sahip olmayan, etkileri, eğer varsa, bu etkiler, örneğin, bir ağ paket olmuştur Gönderilen meydana gelmiş evrenin yeni durumunu iade tarafından işlenir. Bununla birlikte, fonksiyonun sonucu ((),world)ve bu nedenle evrenin yeni halidir world. Spekülatif olarak değerlendirdiğimiz yeni evreni kullanmıyoruz. Evrenin durumu world.

Muhtemelen bunu ciddiye almakta zorlanıyorsunuz. Bunun en iyi yüzeysel olarak paradoksal ve saçmalık olmasının birçok yolu vardır. Eşzamanlılık, bu bakış açısıyla özellikle açık ya da çılgındır.

"Bekle, bekle," diyorsun. " RealWorldsadece bir 'jeton'dur. Aslında tüm evrenin durumu değildir ." Tamam, o zaman bu "yorum" hiçbir şeyi açıklamaz. Bununla birlikte, bir uygulama detayı olarak , GHC modelleri bu şekilde IO. 1 Ancak bu, aslında yan etkileri olan büyülü "işlevlere" sahip olduğumuz anlamına gelir ve bu model anlamlarına rehberlik etmez. Ve bu işlevler aslında yan etkilere sahip olduğundan, dile getirdiğiniz endişe tamamen önemlidir. GHC gelmez emin olmak için kendi yolumdan gitmek zorunda RealWorldve bu özel fonksiyonlar programın amaçlanan davranışını değiştirmek şekillerde optimize edilmemiştir.

Şahsen (muhtemelen şimdiye kadar açıkça görüldüğü gibi), bu "dünyadan geçen" modelin IOpedagojik bir araç olarak işe yaramaz ve kafa karıştırıcı olduğunu düşünüyorum . (Uygulama için yararlı olsun, bilmiyorum. GHC için, daha çok tarihsel bir eser olduğunu düşünüyorum.)

Alternatif bir yaklaşım, IOyanıt işleyicileri ile istekleri açıklayan bir görünüm olarak görmek . Bunu yapmanın çeşitli yolları var. Muhtemelen en erişilebilir, ücretsiz bir monad yapısı kullanmaktır, özellikle şunları kullanabiliriz:

data IO a = Return a | Request OSRequest (OSResponse -> IO a)

Bunu daha sofistike hale getirmenin ve biraz daha iyi özelliklere sahip olmanın birçok yolu var, ancak bu zaten bir gelişme. Anlamak için gerçekliğin doğası hakkında derin felsefi varsayımlar gerektirmez. Tek söylediği IO, ya bir Returndeğer döndürmekten başka bir şey yapmayan önemsiz bir program ya da yanıt için bir işleyici ile işletim sistemine bir istek olmasıdır. OSRequestgibi bir şey olabilir:

data OSRequest = OpenFile FilePath | PutStr String | ...

Benzer şekilde, OSResponseşöyle bir şey olabilir:

data OSResponse = Errno Int | OpenSucceeded Handle | ...

(Yapılabilecek iyileştirmelerden biri, OpenSucceededbir PutStristekten yararlanamayacağınızı bilmeniz için işleri daha güvenli hale getirmektir .) Bu modeller IO, bazı sistem tarafından yorumlanan istekleri ("gerçek" IOmonad için Haskell çalışma zamanının kendisi) ve belki de bu sistem yanıt verdiğimiz işleyiciyi arayacaktır. Bu, elbette, benzer bir talebin nasıl PutStr "hello world"ele alınması gerektiğine dair herhangi bir belirti vermez, ancak aynı şekilde davranmaz. Bunun başka bir sisteme devredildiğini açıkça ortaya koymaktadır. Bu model de oldukça doğru. Modern işletim sistemlerindeki tüm kullanıcı programları, herhangi bir şey yapmak için işletim sisteminden talepte bulunmalıdır.

Bu model doğru sezgileri sağlar. Örneğin, pek çok yeni başlayan kişi <-operatör gibi şeyleri "paketten çıkarma" olarak görür IOya da (maalesef güçlendirilmiş) görüşe sahip olan IO Stringve diyelim ki "içeren" bir konteynerdir String(ve sonra <-bunları çıkarır). Bu istek-yanıt görünümü bu perspektifi açıkça yanlış yapar. İçinde dosya tanıtıcısı yok OpenFile "foo" (\r -> ...). Bunu vurgulamak için yaygın bir benzetme, kek tarifinin içinde kek olmamasıdır (veya bu durumda "fatura" daha iyi olabilir).

Bu model aynı zamanda eşzamanlılık ile kolayca çalışır. Biz kolayca için bir kurucu olabilir OSRequestgibi Fork :: (OSResponse -> IO ()) -> OSRequestve daha sonra çalışma zamanı o seviyor ancak normal bir işleyicisi ile bu ekstra işleyicisi tarafından üretilen istekleri serpiştirebilir. Biraz zekâ ile bunu (ya da ilgili teknikleri) eşzamanlılık gibi şeyleri sadece "işletim sistemine bir talepte bulunuruz ve işler olur" demek yerine daha doğrudan modellemek için kullanabilirsiniz. Bu nasıl IOSpeckütüphane çalışır.

1 Sarılmalar IO, açık bir veri türü yerine opak işlevlerle de olsa, tanımladığım şeye benzeyen, devam tabanlı bir uygulama kullandı . HBC ayrıca eski istek-yanıt akışı tabanlı ES üzerinde katmanlı bir uygulama tabanlı uygulama kullanmıştır. NHC (ve dolayısıyla YHC) thunks kullandı, yani kabaca çağrılmış IO a = () -> aolsa da (), Worlddurum geçişi yapmıyor. JHC ve UHC temel olarak GHC ile aynı yaklaşımı kullandı.


Aydınlatıcı cevabınız için teşekkürler, gerçekten yardımcı oldu. ES'nizin uygulanmasına karar vermem biraz zaman aldı, ancak bunun daha sezgisel olduğunu kabul ediyorum. Bu uygulamanın RealWorld uygulamasında olduğu gibi yan etki sıralaması ile ilgili olası sorunlardan muzdarip olmadığını mı iddia ediyorsunuz? Herhangi bir problemi hemen göremiyorum, ama onların var olmadığı da net değil.
Lasse

Bir yorum: Görünüşe göre OpenFile "foo" (\r -> ...)aslında öyle Request (OpenFile "foo") (\r -> ...)mi?
Lasse

@Lasse Yep, olmalıydı Request. İlk sorunuza cevap vermek için, bu IOdeğerlendirme sırasına (modulo dipleri) açıkça duyarsızdır çünkü atıl bir değerdir. Tüm yan etkiler (eğer varsa), bu değeri yorumlayan şey tarafından yapılacaktır. Örnekte , değerlendirilip değerlendirilmemesi whenönemli değildir action, çünkü Request (PutStr "foo") (...)bu istekleri zaten yorumlayan şeye vermeyeceğimiz bir değer olacaktır. Kaynak kodu gibi; istekli veya tembel bir şekilde azaltmanız önemli değil, tercümanlara verilene kadar hiçbir şey olmuyor.
Derek Elkins SE

Ah evet görüyorum. Bu gerçekten akıllı bir tanım. İlk başta, tüm programın yürütülmesi bittiğinde tüm yan etkilerin mutlaka olması gerektiğini düşündüm, çünkü yorumlayabilmeniz için veri yapısını oluşturmanız gerekir. Ancak bir istek bir devam içerdiğinden, Requestyan etkileri görmeye başlamak için yalnızca ilkinin verilerini oluşturmanız gerekir . Devam değerlendirilirken sonraki yan etkiler oluşturulabilir. Zeki!
Lasse
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.