Должно ли моделирование объектов с состоянием моделироваться с типом эффекта?
Если вы уже используете систему эффектов, она, скорее всего, имеет Ref
тип для безопасной инкапсуляции изменяемого состояния.
Поэтому я говорю: моделировать объекты с состоянием сRef
. Поскольку создание (а также доступ к ним) уже является результатом, это автоматически сделает создание службы эффективным.
Это аккуратно обходит ваш первоначальный вопрос.
Если вы хотите вручную управлять внутренним изменяемым состоянием с помощью обычного, var
вы должны сами убедиться, что все операции, которые касаются этого состояния, считаются эффектами (и, скорее всего, также сделаны поточно-ориентированными), что утомительно и подвержено ошибкам. Это можно сделать, и я согласен с ответом @ atl, что вам не обязательно делать создание объекта с сохранением состояния эффективным (если вы можете жить с потерей ссылочной целостности), но почему бы не избавить себя от проблем и не принять инструменты вашей системы эффектов полностью?
Я думаю, все это чисто и детерминистично. Просто не ссылочно прозрачно, так как результирующий экземпляр каждый раз отличается. Это хорошее время для использования типа эффекта?
Если ваш вопрос можно перефразировать как
Достаточно ли дополнительных преимуществ (помимо правильно работающей реализации, использующей «более слабый класс типов») в отношении ссылочной прозрачности и локальных рассуждений, чтобы оправдать использование типа эффекта (который уже используется для доступа к состоянию и его мутации) также для состояния создание ?
тогда: да, абсолютно .
Чтобы дать пример того, почему это полезно:
Следующее работает нормально, хотя создание сервиса не дает эффекта:
val service = makeService(name)
for {
_ <- service.doX()
_ <- service.doY()
} yield Ack.Done
Но если вы выполните рефакторинг, как показано ниже, вы не получите ошибку во время компиляции, но вы изменили поведение и, скорее всего, ввели ошибку. Если бы вы объявили makeService
эффективный, рефакторинг не будет проверять тип и будет отклонен компилятором.
for {
_ <- makeService(name).doX()
_ <- makeService(name).doY()
} yield Ack.Done
Если присваивать имя методу как makeService
(и с параметром тоже), должно быть достаточно ясно, что метод делает, и что рефакторинг не безопасен, но «локальные рассуждения» означают, что вам не нужно искать в соглашениях об именах и реализации, makeService
чтобы понять это: любое выражение, которое не может быть механически перемешано (дедуплицировано, сделано ленивым, сделано нетерпеливым, мертвый код удалено, распараллелено, отложено, кэшировано, удалено из кэша и т. д.) без изменения поведения ( т.е. не является "чистым") следует набирать как эффективный.
delay
и возвращать F [Service] . В качестве примера, см.start
Метод ввода-вывода , он возвращает IO [Fiber [IO,?]] Вместо простого волокна.