Я не знаю, почему Scala неОказывается, это делает «правильный» ленивые вычисления - скорее всего, это просто не так просто реализовать, особенно если вы хотите, чтобы язык плавно взаимодействовал с JVM.
Вызов по имени (как вы видели) не эквивалентен ленивой оценке, а замене аргумента типа a
аргументом типа () -> a
. Такая функция содержит тот же объем информации, что и обычное значение a
(типы являются изоморфными), но для фактического получения этого значения вам всегда необходимо применить функцию к аргументу фиктивного аргумента ()
. Когда вы дважды оцениваете функцию, вы получите twice the same result, но она должна каждый раз рассчитываться заново (с automatically memoising functions is not feasible).
Ленивых вычисления эквивалентны замена аргумента типа a
с аргументом типа, который ведет себя как следующий класс OO:
class Lazy<A> {
function<A()> computer;
option<A> containedValue;
public:
Lazy(function<A()> computer):
computer = computer
, containerValue = Nothing
{}
A operator()() {
if isNothing(containedValue) {
containedValue = Just(computer());
}
return fromJust(containedValue);
}
}
Это по существу только мемоизация-обертка вокруг конкретного вызова по -name-function type. Что не так приятно, так это то, что эта оболочка фундаментально использует побочные эффекты: когда сначала оценивается ленивое значение, вы должны мутировать containedValue
, чтобы представить тот факт, что значение теперь известно. Haskell имеет этот механизм, запеченный в основе его времени исполнения, хорошо протестированный для обеспечения безопасности потоков и т. Д. Но на языке, который пытается максимально использовать виртуальную виртуальную машину, это, вероятно, вызовет массивные головные боли, если эти побочные мутации чередуются с явными побочными эффектами. Особенно, потому что действительно интересные приложения ленивости не просто имеют один аргумент функции ленивый (который не будет покупать вас много), но разбросайте ленивые значения по всему позвоночнику глубокой структуры данных. В конце концов, это не просто одна функция задержки, которую вы оцениваете позже, чем ввод ленивой функции, это тотал торрентов вложенных вызовов таких функций (действительно, возможно, бесконечно много!), Поскольку потребляется ленивая структура данных.
Итак, Scala избегает опасностей этого, не делая ничего ленивым по умолчанию, хотя, как говорит Алек, он предлагает ключевое слово lazy
, которое в основном добавляет оболочку memoised-function, как указано выше, в значение.
Помимо кеширования, не * вызывают по имени * и * вызов по необходимости * эквивалент? – max
@max Сортировка ... Минус того факта, что имя вызова в Scala поддерживается только на сайте вызова, в отличие от ленивого, поддерживается только на сайте определения – Alec
Кроме того, цитирование @ user7337271 отвечает: 'в побочных эффектах Haskell явны. Scala гораздо более прагматична, и нет явного способа моделирования побочных эффектов'; является ли это основной причиной того, что Scala выбрала оценку по умолчанию для вызова по имени, а не по требованию (учитывая, что функции с побочными эффектами не могут быть замечены)? Или это было связано с пространством или другими соображениями? – max