Czy konstruowanie obiektów stanowych powinno być modelowane za pomocą typu efektu?
Jeśli już używasz systemu efektów, najprawdopodobniej ma on Ref
typ bezpiecznie otaczający stan zmienny.
Mówię więc: modeluj obiekty stanowe za pomocąRef
. Ponieważ tworzenie (a także dostęp do nich) jest już efektem, automatycznie sprawi, że tworzenie usługi również będzie skuteczne.
To starannie omija twoje pierwotne pytanie.
Jeśli chcesz ręcznie regularnie zarządzać wewnętrznym stanem mutable, var
musisz sam upewnić się, że wszystkie operacje, które dotykają tego stanu, są uważane za efekty (i najprawdopodobniej również są bezpieczne dla wątków), co jest uciążliwe i podatne na błędy. Można to zrobić i zgadzam się z odpowiedzią @ atl, że nie musisz ściśle tworzyć skutecznego obiektu państwowego (o ile możesz żyć z utratą integralności referencyjnej), ale dlaczego nie zaoszczędzić sobie kłopotów i objąć narzędzia twojego systemu efektów przez całą drogę?
Myślę, że wszystkie z nich są czyste i deterministyczne. Po prostu nie jest referencyjnie przejrzysty, ponieważ wynikowa instancja jest za każdym razem inna. Czy to dobry moment na użycie efektu typu?
Jeśli twoje pytanie można sformułować jako
Czy dodatkowe korzyści (oprócz poprawnie działającej implementacji przy użyciu „słabszej klasy”) przejrzystości referencyjnej i lokalnego rozumowania są wystarczające, aby uzasadnić użycie typu efektu (który musi być już używany do dostępu do stanu i mutacji) również do stanu kreacja ?
wtedy: tak, absolutnie .
Aby podać przykład, dlaczego jest to przydatne:
Poniższe działa dobrze, nawet jeśli tworzenie usługi nie przynosi efektu:
val service = makeService(name)
for {
_ <- service.doX()
_ <- service.doY()
} yield Ack.Done
Ale jeśli zmienisz to tak, jak poniżej, nie pojawi się błąd czasu kompilacji, ale zmienisz zachowanie i najprawdopodobniej wprowadzisz błąd. Jeśli zadeklarujesz makeService
skuteczność, refaktoryzacja nie sprawdzi typu i zostanie odrzucona przez kompilator.
for {
_ <- makeService(name).doX()
_ <- makeService(name).doY()
} yield Ack.Done
Przyznanie nazwy metody jako makeService
(i wraz z parametrem) powinno wyraźnie wyjaśnić, co robi metoda i że refaktoryzacja nie była bezpieczną rzeczą, ale „lokalne rozumowanie” oznacza, że nie musisz szukać przy konwencjach nazewnictwa i implementacji, makeService
aby wymyślić: Każde wyrażenie, którego nie można mechanicznie przetasować (deduplikowane, leniwe, chętne, eliminuje martwy kod, równoległe, opóźnione, buforowane, usuwane z pamięci podręcznej itp.) bez zmiany zachowania ( tzn. nie jest „czysty”) należy wpisać jako skuteczny.
delay
i zwrócić F [usługę] . Jako przykład, patrzstart
metoda na IO , zwraca IO [Fibre [IO,?]] , Zamiast zwykłego włókna.