Подтвердить что ты не робот

Насколько Хаскелл ленив?

Мне нужно разъяснение лени с Haskell.

Если у меня есть эта функция:

myFunction arg
  | arg == 1 = a
  | arg == 2 = a*b
  | arg == 3 = b+c
  | otherwise = (a+b)*c
     where
            a = ...
            b = ...
            c = ...
            d = ...

Когда я вызываю myFunction 1, Haskell будет оценивать только a = ..., ни b, ни c, ни д.

Но если я пишу

myFunction arg
  | arg == 1 = a
  | arg == 2 = a*b
  | arg == 3 = b+c
  | otherwise = (a+b)*c
     where
            (a,b,c,d) = anotherFunction arg

Каким будет поведение Haskell?

  • Будет ли он оценивать только a и "распространять" ленивость на anotherFunction?
  • Или он будет оценивать весь набор (a, b, c, d) как результат anotherFunction?
4b9b3361

Ответ 1

В обоих случаях он ничего не будет оценивать, если это не требуется. Один из способов потребовать значение - это вызов функции в ghci (который печатает значение в ghci и, следовательно, требует его). Предполагая, что вы выполняете функцию, тогда в вашем втором случае она будет оценивать кортеж слабой головой нормальной формы (WHNF), а затем оценивать первый элемент в (a,b,c,d) потому что требуется только это значение. Остальные элементы b, c и d будут находиться в форме thunk. Фактически вы можете убедиться в этом сами:

myFunction arg
  | arg == 1 = a
  | arg == 2 = a*b
  | arg == 3 = b+c
  | otherwise = (a+b)*c
  where
    (a,b,c,d) = anotherFunction arg

anotherFunction x = (x, undefined, undefined, undefined)

Демо в ghci:

λ> myFunction 1
1

Ответ 2

Ну, его интересует только a, поэтому это означает, что существует неявная функция:

thea :: (a,b,c,d) -> a
thea (a,_,_,_) = a

Другими словами, Haskell не интересуется другими элементами кортежа. Иногда, однако, элементы кортежа имеют некоторую структуру. Скажем, другая функция определяется как:

anotherFunction :: Int -> (Int,Int,Int,Int)
anotherFunction x = (z,t,f,g)
    where f = x*x
          g = f+x
          z = g-2
          t = 3

В этом случае - для оценки первого элемента - также будет оценен третий и четвертый элементы. Но поскольку вы ничего не делаете с ними, Haskell не будет интересоваться их результатом.

Ответ 3

Как уже отмечалось, оценивается только a.

Помните, что для использования лени важно, чтобы anotherFunction возвращал кортеж перед оценкой его компонентов. Например, рассмотрим

anotherFunction n = if p > 1000 then (n, p) else (n, 0)
  where p = product [1..n]

Вышеуказанное всегда будет оценивать product [1..n], даже если вызывающему абоненту требуется только первый компонент пары (который равен n). Это связано с тем, что if необходимо оценить до того, как пара может быть возвращена, и это заставляет p. Напротив,

anotherFunction n = (n, if p > 1000 then p else 0)
  where p = product [1..n]

немедленно вернет пару. Если оценивается только его первый компонент, то p не будет вычислен вообще.

Ответ 4

Если нет необходимости получать значение этой переменной, оно не будет оцениваться. В принципе, Haskell настолько ленив, если не сказано, что это не так.

Вы можете подтвердить это, как это

Prelude> :set +m
Prelude> let anotherFunction = (100, 1 `div` 0)
Prelude| 
Prelude> let myFunction arg
Prelude|                | arg == 1  = a
Prelude|                | otherwise = b
Prelude|                where
Prelude|                     (a, b) = anotherFunction
Prelude| 

Здесь 1 `div` 0 поднимет ошибку divide by zero. Если он оценивает все элементы, то даже когда вы вызываете myFunction с 1, вы получили бы эту ошибку, но

Prelude> myFunction 1
100

только когда вы вызываете его с любым другим значением, необходимо оценить второе значение кортежа, и он потерпит ошибку с ошибкой divide by zero.

Prelude> myFunction 2
*** Exception: divide by zero