Что означает восклицательный знак в декларации на Haskell? - PullRequest
241 голосов
/ 14 июня 2009

Я натолкнулся на следующее определение, пытаясь научить Haskell использовать реальный проект для управления им. Я не понимаю, что означает восклицательный знак перед каждым аргументом, и мои книги, похоже, не упоминали об этом.

data MidiMessage = MidiMessage !Int !MidiMessage

Ответы [ 3 ]

288 голосов
/ 14 июня 2009

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

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.

78 голосов
/ 15 июня 2009

Простой способ увидеть разницу между строгими и не строгими аргументами конструктора - это то, как они ведут себя, когда они не определены. Учитывая

data Foo = Foo Int !Int

first (Foo x _) = x
second (Foo _ y) = y

Поскольку не строгий аргумент не оценивается second, передача undefined не вызывает проблемы:

> second (Foo undefined 1)
1

Но строгим аргументом не может быть undefined, даже если мы не используем значение:

> first (Foo 1 undefined)
*** Exception: Prelude.undefined
24 голосов
/ 14 июня 2009

Я считаю, что это аннотация строгости.

Haskell - это чистый и ленивый функциональный язык, но иногда накладные расходы на ленивость могут быть слишком большими или расточительными. Поэтому, чтобы справиться с этим, вы можете попросить компилятор полностью оценить аргументы функции вместо того, чтобы разбирать thunks вокруг.

На этой странице больше информации: Производительность / Строгость .

...