Это декларация строгости. В основном это означает, что при создании значения структуры данных она должна быть оценена так называемой «слабой нормальной формой головы». Давайте посмотрим на пример, чтобы мы могли видеть, что это значит:
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)
, оставляя трюк внутри неоценимым. Это важно знать, хотя я думаю, что все последствия этого выходят далеко за рамки этого вопроса.
Вы можете аннотировать аргументы функции таким же образом, если вы включите расширение языка BangPatterns
:
f x !y = x*y
f (1+1) (2+2)
вернет thunk (1+1)*4
.