Что восклицательный знак означает в объявлении Haskell?

Кажется, вы действуете, полагая, что if constexpr - это оптимизация производительности. Это не . Если вы поместите константное выражение в предложение ?:, любой компилятор, который стоит использовать, выяснит, к чему он относится, и удалит условие. Таким образом, код, который вы написали, почти наверняка скомпилируется в одну опцию для конкретного Mode.

Основная цель if constexpr состоит в том, чтобы полностью исключить другую ветвь. То есть компилятор даже не проверяет, является ли он синтаксически действительным . Это было бы для чего-то, где вы if constexpr(is_default_constructible_v<T>), и если это правда, вы делаете T(). С помощью регулярного оператора if, если T не является конструируемым по умолчанию, T() все равно должен быть синтаксически допустимым кодом, даже если окружающее предложение if является константным выражением. if constexpr устраняет это требование; компилятор будет отбрасывать операторы, которые не находятся в другом условии.

Это становится еще более сложным для ?:, потому что тип выражения основан на типах двух значений. Таким образом, оба выражения должны быть допустимыми, даже если одно из них никогда не оценивается. Форма constexpr из ?: предположительно отбросит альтернативу, которая не используется во время компиляции. И поэтому тип выражения должен действительно основываться только на одном из них.

Это совсем другая вещь.

246
задан svick 19 April 2014 в 09:49
поделиться

3 ответа

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

data Foo = Foo Int Int !Int !(Maybe Int)

f = Foo (2+2) (3+3) (4+4) (Just (5+5))

Вышеупомянутая функция f при вычислении вернет «преобразователь»: то есть код, который нужно выполнить, чтобы выяснить его ценность. В этот момент Foo еще даже не существует, только код.

Но в какой-то момент кто-то может попытаться заглянуть внутрь него, вероятно, через сопоставление с образцом:

case f of
     Foo 0 _ _ _ -> "first arg is zero"
     _           -> "first arge is something else"

Это будет выполнять достаточно кода, чтобы сделать что ему нужно, и не более того. Таким образом, он создаст Foo с четырьмя параметрами (потому что вы не можете заглянуть внутрь, если он не существует). Во-первых, поскольку мы его тестируем, нам нужно оценить полностью до 4 , где мы понимаем, что он не совпадает.

Второй не нуждается в оценке, потому что мы не тестируем его. Таким образом, вместо того, чтобы хранить 6 в этой ячейке памяти, мы просто сохраним код для возможной последующей оценки, (3 + 3) . Это превратится в 6, только если кто-то посмотрит на него.

Третий параметр, однако, имеет перед ним ! , поэтому он строго оценивается: (4 + 4) выполняется, и 8 сохраняется в этой ячейке памяти.

Четвертый параметр также строго оценивается. Но вот где это становится немного сложнее: мы оцениваем не полностью, а только слабую нормальную форму головы. Это означает, что мы выясняем, является ли это Ничего или Просто что-то, и сохраняем это, но мы не идем дальше. Это означает, что мы сохраняем не Just 10 , а фактически Just (5 + 5) , оставляя преобразователь внутри неоцененным. Это важно знать, хотя я думаю, что все последствия этого выходят за рамки этого вопроса.

Вы можете аннотировать аргументы функции таким же образом, если вы включите языковое расширение BangPatterns :

f x !y = x*y

f (1 + 1) (2 + 2) вернет преобразователь (1 + 1) * 4 .

295
ответ дан 23 November 2019 в 03:04
поделиться

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

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

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

25
ответ дан 23 November 2019 в 03:04
поделиться

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

data Foo = Foo Int !Int

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

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

> second (Foo undefined 1)
1

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

> first (Foo 1 undefined)
*** Exception: Prelude.undefined
82
ответ дан 23 November 2019 в 03:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: