Haskell does not have references (a reference is a mutable object, and Haskell does not have (directly-accessible) mutable objects). Therefore function calls use value semantics, kind of by default. This is in fact an important property of pure functional languages: a function cannot modify its argument.
Value semantics does not imply that copying happens under the hood. You only need to copy the part of a value that the function modifies, which in a pure language means you never need to copy anything.
However, this isn't the whole story. In a sense, Haskell has reference semantics.
While it is meaningless to test whether a function modifies its argument (it never does), you can test whether a function uses (part of) its argument. Supply it with an argument that does not terminate. If the function call terminates, you know the function didn't use its argument.
let bottom = bottom
let ignore x = 1
ignore bottom
If you evaluate bottom
, it does not terminate: bottom
expands to itself, ad nauseam. The term bottom
cannot have a value. But if you evaluate ignore bottom
, the value is 1
. This shows that calling the function ignore
does not require to calculating the value of its argument. In this sense, Haskell has a reference semantics: what a function receives is not a value but something that allows this value to be found. The technical term is call by name (as opposed to call by value).
(More precisely, Haskell implementations use call by need. In call by value, the argument of a function is evaluated exactly once, just before calling the function. In call by name, the argument is evaluated each time it is used, which can range from never to as many times as the function wants. In call by need, the argument is evaluated at most once: it is evaluated the first time it is used, or never if it isn't used.)
a
"?