Является ли что-то в предложении "where" в Haskell только один раз?

Когда у меня есть следующий код:

func n = m ++ [1] ++ m ++ [0] ++ m
    where m = func2 n

Сколько раз вызывает функцию func2? Только один раз, в разделе where? Или он просто вычисляется снова каждый раз, когда я использую m?

Ответы

Ответ 1

Может быть, это вообще не оценивается (радость лени) - но если это так, его нужно оценивать только один раз - если хотите, вы можете попробовать себя trace:

import Debug.Trace(trace)

func n = m ++ [1] ++ m ++ [0] ++ m
  where m = func2 n

func2 n = trace "called..." [n]

Вот пример в GHCi:

λ> func 3
called...
[3,1,3,0,3]

И вот здесь вы видите, что он не может быть вызван (пока вам не понадобится его оценить):

λ> let v = func 4

λ> v
called...
[4,1,4,0,4]

Смотрите: сначала он не вызывается - только когда вы наконец оцените v (чтобы его распечатать), вы получаете вызов.

Ответ 2

Ответ Carsten (значение будет вычисляться не чаще одного раза) правильно, если вы не отключили ограничение мономорфизма. Если у вас есть, то m может иметь полиморфный предполагаемый тип, который включает класс типа, а затем m на самом деле не является нормальным значением, а скорее функцией, которая принимает словарь типа и вырабатывает значение. Рассмотрим этот пример.

{-# LANGUAGE NoMonomorphismRestriction #-}

import Debug.Trace(trace)

func n = m ++ [1] ++ m ++ [0] ++ m
  where m = func2 n                     -- m :: Monad t => t a (where n :: a)

func2 n = trace "called..." (return n)  -- func2 :: Monad t => a -> t a

Затем оценка func 3 в графах ghci

called...
[3,1called...
,3,0called...
,3]

Ответ 3

Чтобы добавить ответы @Carstan и @Reid Barton, это также зависит от того, выполняется ли вы скомпилированный код или нет, например:

{-# LANGUAGE NoMonomorphismRestriction #-}

import Debug.Trace(trace)

func n = m ++ [1] ++ m ++ [0] ++ m
  where m = func2 n                     -- m :: Monad t => t a (where n :: a)

func2 n = trace "called..." (return n)

main = let xs = func 3 :: [Int]
       in print xs

В ghci работает main печатает called 3 раза. Однако при компиляции он только распечатывает called один раз при использовании -O2. (Протестировано с помощью 7.10.2.)