Почему существует только одна нестрогая функция от Int до Int?
В соответствии с этой статьей о денотационной семантике для Haskell существует только одна нестрогая функция (non-bottom prreserving) от Int до Int.
:
бывает, что существует только один прототип нестрогой функции типа Integer → Integer:
один x = 1
Его варианты constk x = k для каждого конкретного числа k. Почему это единственные возможности? Помните, что один n может быть не менее определенным, чем один ⊥. Поскольку Integer является плоской областью, оба должны быть равны.
По сути, это говорит о том, что единственными нестрогими функциями сигнатуры этого типа могут быть только постоянные функции. Я не следую этому аргументу. Я также не уверен, что подразумевается под плоской областью, остальная часть статьи приводит к мнению, что это просто означает, что у poset значений есть только один node: bottom.
Что-то подобное происходит для функции, идущей от A- > A, или A- > B? То есть они должны быть постоянными функциями?
Ответы
Ответ 1
Интуиция заключается в том, что ленивая функция не может проверить свой аргумент здесь, не заставляя ее (и, следовательно, становиться строгой). Если вы не проверяете свой аргумент, вы должны быть const
Реальный ответ в монотонности. Если вы думаете о семантических доменах как о позициях, где отношения упорядочения являются "определенными", все функции сохраняются в порядке. Зачем? Поскольку все днища созданы равными, а цикл навсегда - это то же самое, что и нижнее, то немонотонная функция будет той, которая решает проблему остановки.
Хорошо, так почему же это означает, что const
создает только ленивые функции? Ну, скажем, выберите произвольную функцию f
такую, что
f :: Integer -> Integer
f ⊥ = y
поскольку ⊥ <= x
для всех x
, это должно быть y <= f x
. Если y
- не нижнее значение, то единственным решением этого неравенства является f x = y
Изменить: причина этого аргумента в отношении таких типов, как Integer
и Bool
, но не для таких типов, как [a]
, является последним: Integer
в некотором смысле имеет только один ⊥
. То есть, все целые числа определены одинаково, за исключением ⊥
. С другой стороны, ⊥ < (⊥:⊥)
в то время как (⊥:⊥) < (⊥:[])
и (⊥:⊥) < (⊥:(⊥:⊥)) < (⊥:(⊥:(⊥:⊥))) < ...
больше, (⊥:⊥) < ('a':⊥)
. То есть семантическая область [a]
достаточно богата, что y <= f x
с y =/= ⊥
не означает, что f x = y
.
Ответ 2
Любая функция на Integer
, которая не является const k
для некоторой константы k
, должна проверять ее аргумент. Вы не можете частично проверить Integer
, который может быть - это то, что означает "плоский домен". Это является следствием того, как семантика Integer
определена в спецификациях Haskell, а не что-то, что следует из семантики основного языка.
В отличие от этого, для каждого типа a
существует бесконечно много нестрогих функций типа [a] -> [a]
, например. take1
:
take1 (x:_) = [x]
Чтобы показать не строгость, определите
ones = 1 : ones
В терминах денотационной семантики [[ ones
]] = ⊥. Но take1 ones
оценивается как [1]
, поэтому take1
является нестрогим. Так что take2 (x:y:_) = [x,y]
, take10
и т.д.
Если вам нужны нестрогие непостоянные функции для целых чисел, вам нужно другое представление целых чисел, чем Integer
, например:
data Bit = Zero | One
newtype BinaryInt = I [Bit]
Если мы интерпретируем список в I
как "двоичное" двоичное целое число, то функция
mod2 (I []) = I []
mod2 (I (lsb:_)) = I [lsb]
является нестрогим.