В высокопроизводительном коде Haskell (например, числовом коде) устранение отложенных вычислений из внутреннего цикла может быть значительным преимуществом. GHC поддерживает три расширения, позволяющие программисту указать использование строгой (вычисление по значению) оценки вместо ленивой (вычисление по мере необходимости) оценки.
- Шаблоны bang (
BangPatterns) делают сопоставление с образцом и связи let более строгими. - Строгие типы данных (
StrictData) по умолчанию делают поля конструкторов строгими для каждого модуля. - Строгий шаблон (
Strict) делает все шаблоны и связи let строгими по умолчанию для каждого модуля.
Последние два расширения просто позволяют избежать заполнения высокопроизводительного кода шаблонами bang, что затрудняет его чтение.
Шаблоны bang и строгое соответствие никоим образом не влияют на систему типов.
6.14.1. Шаблоны bang
-
BangPatterns -
Разрешает использование синтаксиса шаблонов bang.
GHC поддерживает расширение сопоставления с образцом, называемое шаблонами bang, написанными !pat. Шаблоны bang доступны по умолчанию как часть GHC2021.
Основная идея заключается в добавлении одного нового правила к синтаксису шаблонов:
pat ::= !pat
Сопоставление выражения e с шаблоном !p выполняется путем предварительной оценки e (до нормальной формы вычисления) и затем сопоставления результата с p. Пример:
f1 !x = True
Это определение делает f1 строгим относительно x, в то время как без bang оно было бы ленивым.
Обратите внимание на следующие моменты:
-
Шаблоны bang могут быть вложенными:
f2 (!x, y) = [x,y]
Здесь
f2строгий относительноx, но не относительноy. -
Шаблоны bang могут использоваться и в выражениях
case:g1 x = let y = f x in body g2 x = case f x of { y -> body } g3 x = case f x of { !y -> body }Функции
g1иg2означают ровно то же самое. Ноg3оценивает(f x), связываетyс результатом и затем оцениваетbody. -
Шаблоны bang не оказывают никакого влияния на шаблоны конструкторов:
f3 !(x,y) = [x,y] f4 (x,y) = [x,y]
Здесь
f3иf4идентичны; добавление bang перед шаблоном, который в любом случае принуждает к оценке, ничего не меняет. Однако см. оговорку ниже. -
Существует одна проблема с синтаксической неоднозначностью. Рассмотрим:
f !x = 3
Является ли это определением инфиксной функции «
(!)», или «f» с шаблоном bang? GHC разрешает эту неоднозначность, анализируя окружающие пробелы:a ! b = ... -- infix operator a !b = ... -- bang pattern
См. GHC Предложение № 229 для точных правил.
6.14.1.1. Строгие связи
Расширение BangPatterns дополнительно включает синтаксис для строгих let или where связей с !pat = expr. Например,
let !x = e in body let !(p,q) = e in body
В обоих случаях e оценивается до начала оценки body.
Обратите внимание на следующие моменты:
- Строгая связь (с верхнеуровневым
!) не следует рассматривать как обычную связь по образцу, которая случайно имеет шаблон bang (Шаблоны bang) в левой части. Скорее, верхнеуровневый!следует рассматривать как часть связи let, а не как часть шаблона. Это имеет значение, когда мы переходим к правилам в Динамическая семантика шаблонов bang. -
Только верхнеуровневый bang (возможно, в скобках) делает связь строгой; в противном случае она считается обычным шаблоном bang. Например,
let (!x,[y]) = e in b
эквивалентно этому:
let { t = case e of (x,[y]) -> x `seq` (x,y) x = fst t y = snd t } in bСвязь ленивая, но при оценке
xилиyпосредствомbвесь шаблон сопоставляется, включая принудительную оценкуx. -
Поскольку
!в строгой связи не является шаблоном bang, оно должно быть видно без прохождения через синонимы шаблоновpattern Bang x <- !x f1 = let Bang x = y in ... f2 = let !x = y in ... -- not equivalent to f1
- Строгие связи не допускаются на верхнем уровне модуля.
- См. Семантика связей let с шаблонами bang для подробной семантики и описание функции в Haskell prime для более подробных обсуждений и примеров.
6.14.2. Типы данных со строгостью по умолчанию
-
StrictData -
- Since:
-
8.0.1
По умолчанию поля типов данных, определённых в текущем модуле, считаются строгими.
Неформально расширение языка StrictData изменяет объявления типов данных, чтобы поля по умолчанию были строгими. Поля могут быть ленивыми путём добавления ~ перед полем.
Когда пользователь пишет
data T = C a data T' = C' ~a
мы интерпретируем это, как если бы они написали
data T = C !a data T' = C' a
Это расширение влияет только на определения в данном модуле.
Аннотация ~ должна быть написана в префиксной форме:
data T = MkT ~Int -- valid data T = MkT ~ Int -- invalid
См. GHC Предложение № 229 для точных правил.