Я изучаю Мартин Одерски Principles of Reactive Programming. Говоря о реализации простой структуры FRP, он вначале дал команду, которая использует StackableVariable
(т. Е. DynamicVairable
), чтобы отслеживать текущий обновленный сигнал, который я могу понять. Но в конце слайдов он упомянул, что более чистым решением является использование неявного параметра вместо DynamicVariable
. Может ли кто-нибудь показать мне, как это можно сделать?Как реализовать FRP с использованием неявного параметра?
ответ
Ссылка на слайды не работает для меня. Когда я попытался использовать его, я теперь использую 1 в качестве ссылки.
Динамическая переменная - это локальная переменная потока, которая содержит состояние текущего сигнала. Это необходимо, чтобы метод Apply Signal мог получить доступ к этой информации. Давайте рассмотрим следующий пример кода:
val a: Signal[Int] = ???
val b: Signal[Int] = ???
val xPlusY: Signal[Int] = Signal(a() + b())
Здесь, когда a()
называется, она добавляет себя в список зависимостей в настоящее время оценки сигнала. Поскольку эта информация недоступна где-либо еще, мы в основном используем глобальную переменную a.k.a.
У этого решения есть несколько проблем. Например, мы также можем позвонить a()
, если мы не внутри Signal()
. Кроме того, мы должны использовать глобальную переменную.
Решения будет давать эту информацию a()
через неявный аргумент: Меняет подпись от
Signal[T]#apply(): T
в
Signal[T]#apply()(implicit triggeredBy: Signal[_])
(Обратите внимание, что мы, вероятно, хотим использовать некоторые новый тип TriggeredBy
, который инкапсулирует сигнал)
Таким образом, реализация этого метода будет иметь доступ к i ts, генерирующий сигнал без глобальной/потоковой локальной переменной.
Но теперь мы должны каким-то образом представить это неявное. Одним из вариантов является также изменить сигнатуру функции сигнал создания из
def Signal.apply[T](fun: => T): Signal[T]
в
def Signal.apply[T](fun: Signal[_] => T): Signal[T]
К сожалению, синтаксис нашего примера кода должен изменить то, потому что мы должны обеспечить функцию вместо тела:
val xPlusY: Signal[Int] = Signal { implicit triggeredBy => a() + b() }
Есть несколько способов решения этой проблемы:
Подождите, пока не будут реализованы неявные типы функций 2. Это, вероятно, не произойдет в ближайшее время, но это позволит нам написать
Signal.apply
подпись следующим образом:def Signal.apply[T](fun: implicit Signal[_] => T): Signal[T]
и затем иметь возможность написать
Signal(a() + b())
снова.Использование макрома магии для преобразования кода формы
Signal(a() + b())
в кодSignal.internalApply(implicit triggeredBy => a() + b())
. Это означает, чтоSignal.apply
теперь является макросом. Это путь, по которому идет scala.rx 3, и он хорошо работает с точки зрения использования. Это также позволяет нам снова написатьSignal(a() + b())
.
Update: обновлена ссылка на неявной функции объяснения к более детальному блога artikle
Непонятная функция типа вещей, похоже, делает что-то странное в примере Odersky. Вы уверены, что он также будет работать так, как вы его используете? –
ну, так как вся моя информация об этом - от просмотра https://youtu.be/GHzWqJKFCk4?t=47m19s, и я не нашел никакой дополнительной информации об этом, и она нигде не будет реализована; Я не уверен, что это также будет работать в этом примере, но я понимаю, что он, по крайней мере, должен работать таким же образом. –
@ Jasper-M есть новая статья в блоге Мартина Одерски, в которой четко указано, что неявные типы функций действительно будут работать как ожидалось (но, к сожалению, только в пунктире); Я обновил ссылку в статье –
Я думаю, что scala.rx делает это следующим образом: https://github.com/lihaoyi/scala.rx/ blob/master/scalarx/shared/src/main/scala/rx/Core.scala – Reactormonk
ссылка, похоже, не работает, я получаю сообщение об отказе доступа –