Jest to sugerowana „interpretacja” IO
monady. Jeśli chcesz poważnie potraktować tę „interpretację”, musisz poważnie potraktować „RealWorld”. Nie ma znaczenia, czy action world
zostanie spekulacyjnie oceniony, czy nie, action
nie ma żadnych skutków ubocznych, jego ewentualne skutki są obsługiwane przez zwrócenie nowego stanu wszechświata, w którym wystąpiły te efekty, np. Wysłano pakiet sieciowy. Jednak wynikiem tej funkcji jest ((),world)
nowy stan wszechświata world
. Nie używamy nowego wszechświata, który mogliśmy spekulować na boku. Stan wszechświata jest world
.
Prawdopodobnie trudno ci brać to na poważnie. Jest na wiele sposobów, w najlepszym wypadku, powierzchownie paradoksalny i bezsensowny. Z tej perspektywy współbieżność jest albo nieoczywista, albo szalona.
„Czekaj, czekaj” - mówisz. „ RealWorld
to tylko„ token ”. To nie jest stan całego wszechświata.” Okej, to ta „interpretacja” nic nie wyjaśnia. Niemniej jednak, jako szczegół implementacji , tak właśnie modele GHC IO
. 1 Oznacza to jednak, że mamy magiczne „funkcje”, które faktycznie wywołują skutki uboczne i ten model nie zawiera wskazówek co do ich znaczenia. A ponieważ te funkcje faktycznie wywołują skutki uboczne, twoja obawa jest całkowicie na miejscu. GHC nie trzeba wychodzić z jego sposób, aby upewnić się RealWorld
, a te specjalne funkcje nie są zoptymalizowane w taki sposób, że zmiany zamierzonego zachowania programu.
Osobiście (jak to zapewne jest obecnie widoczne), uważam, że ten „przemijający świat” model IO
jest po prostu bezużyteczny i mylący jako narzędzie pedagogiczne. (Nie wiem, czy jest to przydatne do implementacji. W przypadku GHC uważam, że jest to raczej historyczny artefakt).
Jednym z alternatywnych podejść jest wyświetlanie IO
żądań opisujących za pomocą procedur obsługi odpowiedzi. Można to zrobić na kilka sposobów. Prawdopodobnie najbardziej dostępna jest darmowa konstrukcja monady, w szczególności możemy użyć:
data IO a = Return a | Request OSRequest (OSResponse -> IO a)
Istnieje wiele sposobów, aby uczynić to bardziej wyrafinowanym i mieć nieco lepsze właściwości, ale jest to już poprawa. Nie wymaga głębokich filozoficznych założeń dotyczących natury rzeczywistości. Stwierdza tylko, że IO
jest to albo trywialny program Return
, który nic nie robi, ale zwraca wartość, albo jest to żądanie do systemu operacyjnego z funkcją obsługi odpowiedzi. OSRequest
może być coś takiego:
data OSRequest = OpenFile FilePath | PutStr String | ...
Podobnie OSResponse
może być coś takiego:
data OSResponse = Errno Int | OpenSucceeded Handle | ...
(Jednym z ulepszeń, które mogą być wykonane jest, aby rzeczy bardziej wpisz bezpieczny tak, że wiesz, że nie będzie się OpenSucceeded
z PutStr
wniosku). To modele IO
jak opisujący wnioski, które uzyskać interpretowane przez jakiegoś układu (za „prawdziwe” IO
monada jest sam środowisko uruchomieniowe Haskell), a następnie być może ten system wywoła moduł obsługi, którego udzieliliśmy odpowiedzi. To oczywiście nie daje żadnych wskazówek na temat tego, jak PutStr "hello world"
należy obsługiwać takie żądanie , ale też nie udaje. Wyjaśnia, że jest to delegowane do innego systemu. Ten model jest również dość dokładny. Wszystkie programy użytkownika we współczesnych systemach operacyjnych muszą wysyłać żądania do systemu operacyjnego, aby cokolwiek zrobić.
Ten model zapewnia właściwe intuicje. Na przykład wielu początkujących uważa rzeczy takie jak <-
operator za „rozpakowywanie” IO
lub mają (niestety wzmocnione) widoki, które IO String
, powiedzmy, są „pojemnikami”, które „zawierają” String
(a następnie <-
wyciągają je). Ten widok żądanie-odpowiedź sprawia, że ta perspektywa jest wyraźnie błędna. Wewnątrz nie ma uchwytu pliku OpenFile "foo" (\r -> ...)
. Popularną analogią do podkreślenia tego jest to, że w przepisie na ciasto nie ma ciasta (a może w tym przypadku lepiej byłoby „wystawić fakturę”).
Ten model działa również z współbieżnością. Możemy łatwo mieć konstruktor dla OSRequest
like, Fork :: (OSResponse -> IO ()) -> OSRequest
a następnie środowisko wykonawcze może przeplatać żądania wygenerowane przez ten dodatkowy moduł obsługi z normalnym modułem obsługi, jak mu się podoba. Przy odrobinie sprytu możesz użyć tej (lub pokrewnych technik) do modelowania rzeczy takich jak współbieżność bardziej bezpośrednio, niż po prostu mówiąc „wysyłamy zapytanie do systemu operacyjnego i rzeczy się dzieją”. Tak działa IOSpec
biblioteka .
1 Uściski zastosowały kontynuację, IO
która jest mniej więcej podobna do tego, co opisuję, aczkolwiek z nieprzezroczystymi funkcjami zamiast jawnego typu danych. HBC wykorzystał również implementację opartą na kontynuacji warstwową na starym IO opartym na strumieniu żądanie-odpowiedź. NHC (a tym samym YHC) użył thunksów, tzn. Z grubsza, IO a = () -> a
jak ()
to się nazywało World
, ale nie robi stanu. JHC i UHC zastosowały w zasadzie to samo podejście co GHC.