Это декларация строгости. По сути, это означает, что при создании значения структуры данных оно должно быть оценено как «нормальная форма слабой головы». Давайте рассмотрим пример, чтобы понять, что это значит:
data Foo = Foo Int Int !Int !(Maybe Int)
f = Foo (2+2) (3+3) (4+4) (Just (5+5))
Приведенная f
выше функция при оценке вернет «thunk»: код, который нужно выполнить, чтобы выяснить его значение. На данный момент, Foo даже еще не существует, только код.
Но в какой-то момент кто-то может попытаться заглянуть внутрь него, возможно, с помощью сопоставления с образцом:
case f of
Foo 0 _ _ _ -> "first arg is zero"
_ -> "first arge is something else"
Это будет выполнять достаточно кода, чтобы делать то, что ему нужно, и не более того. Таким образом, он создаст Foo с четырьмя параметрами (потому что вы не можете заглянуть внутрь него, если он не существует). Во-первых, так как мы тестируем его, нам нужно оценить весь путь туда 4
, где мы понимаем, что он не соответствует.
Второе не нужно оценивать, потому что мы его не тестируем. Таким образом, вместо того, 6
чтобы храниться в этой ячейке памяти, мы просто сохраним код для возможной последующей оценки (3+3)
. Это превратится в 6, только если кто-то смотрит на это.
Третий параметр, однако, имеет !
перед ним, поэтому строго оценивается: (4+4)
выполняется и 8
хранится в этой ячейке памяти.
Четвертый параметр также строго оценен. Но вот где это становится немного сложнее: мы оцениваем не полностью, а только для слабой нормальной формы головы. Это означает, что мы выясняем, является ли это Nothing
или Just
что-то, и сохраняем это, но мы не идем дальше. Это означает, что мы храним не так, Just 10
а на самом деле Just (5+5)
, оставляя thunk внутри неоцененным. Это важно знать, хотя я думаю, что все последствия этого выходят за рамки этого вопроса.
Вы можете аннотировать аргументы функции таким же образом, если вы включите BangPatterns
расширение языка:
f x !y = x*y
f (1+1) (2+2)
вернет гром (1+1)*4
.