2015-02-24 3 views
3

Я пытаюсь выяснить, есть ли макрос, похожий на delay в clojure, чтобы получить ленивое выражение/переменную, которую можно оценить позже.Lazy оценка выражения в Elixir

Используемый случай является значением по умолчанию для Map.get/3, так как значение по умолчанию происходит от вызова базы данных, я бы предпочел, чтобы он вызывается только тогда, когда это необходимо.

+0

Рассматривая источник «задержки» в Clojure (здесь: https://github.com/clojure/clojure/blob/201a0dd9701e1a0ee3998431241388eb4a854ebf/src/jvm/clojure/lang/Delay.java), похоже, что задержка ближе к протоколу, чем макрос. Если бы я был вами, я бы исследовал протоколы в Elixir и продолжал таким образом. –

+0

Я проверил источник 'delay', прежде чем задавать вопрос, и интересно, что' delay' была реализована в java, а не clojure, что позволяет использовать много разных конструкций и манипулировать выражением. – NhanH

ответ

1

макрос Эликсир можно было бы использовать для написания простой функции обертку для условной оценки. Я поставил один смысл в следующем, хотя это может быть лучше/умнее.

https://gist.github.com/parroty/98a68f2e8a735434bd60

+0

Это решение, в котором я тоже оказался, хотя мой код использовался, если он еще, и выглядит гораздо менее приятным. Похоже, что нет лучшего способа, если мы фактически не получили поддержку компилятора :-) И один недостаток, сравниваемый с 'delay' в clojure, заключается в том, что мы должны написать макрос для каждой функции, которую мы хотим использовать, а не имеющих один универсальный оператор. – NhanH

0

Одним из способов сделать это будет использование процессов. Например, карта может быть завернута в процесс, такой как GenServer или Агент (http://elixir-lang.org/docs/v1.0/elixir/Agent.html), где значение по умолчанию будет оцениваться ленивым.

+0

Я не совсем понимаю, как вы могли бы использовать процесс для ленивого eval, не возражаете ли вы использовать некоторый код в качестве примера? – NhanH

1

Значение по умолчанию может быть функцией, которая делает дорогой вызов. Если Map.get/3 не используется для возврата функций, вы можете проверить, является ли это значение функцией и вызывать его, если оно возвращается. Вроде так:

def default_value() 
    expensive_db_call() 
end 

def get_something(dict, key) do 
    case Map.get(dict, key, default_value) do 
    value when is_fun(value) -> 
     value.() # invoke the default function and return the result of the call 
    value -> 
     value # key must have existed, return value 
    end 
end 

Конечно, если карта содержит функции, этот тип решения, вероятно, не будет работать.

Также проверьте модуль Elixir's Stream. Хотя я не знаю, что это поможет решить вашу конкретную проблему, это позволяет ленивую оценку. Из документации:

Потоки являются составными, ленивыми перечислениями. Любой перечислимый, который генерирует элементы один за другим во время перечисления, называется потоком. Например, Range Эликсир является поток:

Более подробная информация доступна в документации: http://elixir-lang.org/docs/master/elixir/Stream.html

+0

Поток не то, что я искал, я искал способ отложить произвольное выражение. Ваше решение с 'case' довольно близко к реальному решению, но для решения потребуется использовать макрос для работы с любым произвольным выражением. – NhanH

0

«Generic» лени немного жесткой орешек, потому что это довольно широкий вопрос. Потоки позволяют лень для перечислений, но я не уверен, что означало бы ленивость выражения. Например, какая была бы ленивая форма x = 1 + 2? Когда это будет оценено?

мысль, что приходит на ум для ленивой формы выражения является выражением процедуры:

def x, do: 1 + 2 

Поскольку значение й не будет вычислено, пока выражение не будет на самом деле вызываются (насколько я знаю). Я уверен, что другие меня исправит, если я ошибаюсь в этот момент. Но я не думаю, что это то, что вы хотите.

Возможно, вы хотите перефразировать свой вопрос - оставив потоки и ленивую оценку перечисляемых значений.

Смежные вопросы