Это декларация строгости. По сути, это означает, что при создании значения структуры данных оно должно быть оценено как «нормальная форма слабой головы». Давайте рассмотрим пример, чтобы понять, что это значит:
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.