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