Haskell Language
взыскательность
Поиск…
Шаблоны Bang
Шаблоны, аннотированные с ударом ( !
), Оцениваются строго, а не лениво.
foo (!x, y) !z = [x, y, z]
В этом примере x
и z
оба будут вычисляться до нормальной нормальной формы головы, прежде чем возвращать список. Это эквивалентно:
foo (x, y) z = x `seq` z `seq` [x, y, z]
Шаблоны Bang активируются с использованием языкового расширения Haskell 2010 BangPatterns
.
Нормальные формы
В этом примере приводится краткий обзор - для более подробного объяснения нормальных форм и примеров см. Этот вопрос .
Уменьшенная нормальная форма
Приведенная нормальная форма (или просто нормальная форма, когда контекст ясен) выражения является результатом оценки всех приводимых подвыражений в данном выражении. Из-за нестрогой семантики Haskell (обычно называемой лень ) подвыражение не может быть приводимым, если оно находится под связующим (т.е. абстракция лямбда - \x -> ..
). Нормальная форма выражения обладает тем свойством, что если оно существует, оно уникально.
Другими словами, это не имеет значения (в терминах денотационной семантики), в каком порядке вы уменьшаете подвыражения. Однако ключ к написанию исполняемых программ Haskell часто гарантирует, что правильное выражение оценивается в нужное время, т. Е. Понимание оперативной семантики.
Выражение, нормальная форма которого сама по себе, называется нормальной .
Некоторые выражения, например, let x = 1:x in x
, не имеют нормальной формы, но все же продуктивны. Выражение выражения все еще имеет значение , если оно допускает бесконечные значения, которые представляют собой список [1,1, ...]
. Другие выражения, такие как let y = 1+y in y
, не имеют значения или их значение не undefined
.
Нормальная форма слабой головы
RNF соответствует полной оценке выражения - аналогично, нормальная форма слабой головки (WHNF) соответствует оценке головы выражения. Глава выражения e
полностью оценивается, если e
- приложение Con e1 e2 .. en
и Con
- конструктор; или абстракции \x -> e1
; или частичное приложение f e1 e2 .. en
, где частичное приложение означает, что f
принимает больше n
аргументов (или, что эквивалентно, тип e
является типом функции). В любом случае подвыражения e1..en
могут быть оценены или не оценены для выражения в WHNF - они могут даже быть undefined
.
Семантика оценки Haskell может быть описана в терминах WHNF - для оценки выражения e
, сначала оцените его в WHNF, а затем рекурсивно оцените все его подвыражения слева направо.
Первоначальная функция seq
используется для оценки выражения WHNF. seq xy
денотационно равно y
(значение seq xy
точно равно y
); кроме того, x
оценивается как WHNF, когда y
оценивается WHNF. Выражение также может быть оценено в WHNF с шаблоном помех (активируется расширением -XBangPatterns
), синтаксис которого следующий:
f !x y = ...
В котором x
будет оцениваться в WHNF, когда f
оценивается, а y
не (обязательно) оценивается. Шаблон помех может также отображаться в конструкторе, например
data X = Con A !B C .. N
в этом случае конструктор Con
считается строгим в поле B
, что означает, что поле B
оценивается как WHNF, когда конструктор применяется к достаточным (здесь, двум) аргументам.
Ленивые узоры
Ленивые или неопровержимые шаблоны (обозначаемые синтаксисом ~pat
) - это шаблоны, которые всегда совпадают, даже не рассматривая сопоставимое значение. Это означает, что ленивые шаблоны будут соответствовать даже нижним значениям. Однако последующее использование переменных, связанных в подструктурах неопровержимого шаблона, заставит совпадение шаблонов происходить, оценивая до дна, если совпадение не будет выполнено.
Следующая функция в своем аргументе ленива:
f1 :: Either e Int -> Int
f1 ~(Right 1) = 42
и поэтому мы получаем
λ» f1 (Right 1)
42
λ» f1 (Right 2)
42
λ» f1 (Left "foo")
42
λ» f1 (error "oops!")
42
λ» f1 "oops!"
*** type mismatch ***
Следующая функция написана с ленивым шаблоном, но на самом деле использует переменную шаблона, которая заставляет совпадение, поэтому не будет Left
аргументом:
f2 :: Either e Int -> Int
f2 ~(Right x) = x + 1
λ» f2 (Right 1)
2
λ» f2 (Right 2)
3
λ» f2 (Right (error "oops!"))
*** Exception: oops!
λ» f2 (Left "foo")
*** Exception: lazypat.hs:5:1-21: Irrefutable pattern failed for pattern (Right x)
λ» f2 (error "oops!")
*** Exception: oops!
let
привязки ленивы, ведут себя как неопровержимые шаблоны:
act1 :: IO ()
act1 = do
ss <- readLn
let [s1, s2] = ss :: [String]
putStrLn "Done"
act2 :: IO ()
act2 = do
ss <- readLn
let [s1, s2] = ss
putStrLn s1
Здесь act1
работает на входах, которые анализируются на любой список строк, тогда как в act2
для putStrLn s1
требуется значение s1
которое заставляет совпадение шаблонов для [s1, s2]
, поэтому оно работает только для списков ровно двух строк:
λ» act1
> ["foo"]
Done
λ» act2
> ["foo"]
*** readIO: no parse ***
Строгие поля
В объявлении data
префикс типа с bang ( !
) Делает поле строгим полем . Когда применяется конструктор данных, эти поля будут вычисляться до нормальной нормальной формы головы, поэтому данные в полях гарантированно всегда будут в слабой нормальной форме головы.
Строгие поля могут использоваться как для записей, так и для не-записей:
data User = User
{ identifier :: !Int
, firstName :: !Text
, lastName :: !Text
}
data T = MkT !Int !Int