Подтвердить что ты не робот

В GHC.TypeLits что такое someNatVal хорошо (чего мы не можем выполнить с natVal)?

Я пытаюсь понять GHC.TypeLits и, в частности, someNatVal. Я понимаю, как он использовался в этом блоге здесь, здесь, но, как уже упоминалось, такой же пример можно было реализовать с помощью natVal, например:

isLength :: forall len a. KnownNat len => Integer -> List len a -> Bool
isLength n _ = n == natVal (Proxy :: Proxy len)

Существуют ли какие-либо применения someNatVal, которые нельзя переписать с помощью natVal?

4b9b3361

Ответ 1

SomeNat определяется по существу:

data SomeNat = forall n. KnownNat n => SomeNat (Proxy n)

который читается как SomeNat, содержит, ну, "some n :: Nat". Proxy - это одноэлементный режим, который позволяет поднять этот уровень n до уровня уровня, чтобы удовлетворить типу системы. На зависимом языке мы нуждаемся в такой конструкции очень редко. Мы можем определить SomeNat более явным образом с помощью GADTs:

data SomeNat where
    SomeNat :: forall n. KnownNat n => Proxy n -> SomeNat

So SomeNat содержит a Nat, который не известен статически.

Тогда

someNatVal :: Integer -> Maybe SomeNat

читается как "someNatVal получает Integer и Maybe возвращает hiden Nat". Мы не можем вернуть KnownNat, потому что Known означает "известно на уровне уровня", но мы ничего не знаем о произвольном Integer на уровне типа.

Обратная (по модулю SomeNat оболочка и Proxy вместо Proxy) someNatVal равна

natVal :: forall n proxy. KnownNat n => proxy n -> Integer

Считывается как "natVal получает то, что имеет Nat в своем типе, и возвращает Nat, преобразованный в Integer".

Экстрастенциально квантифицированные типы данных обычно используются для переноса значений времени выполнения. Предположим, вы хотите прочитать Vec (список со статически известной длиной) из STDIN, но как бы вы статически вычисляли длину ввода? Выхода нет. Таким образом, вы завершаете список, который вы прочитали, в соответствующем экзистенциально квантифицированном типе данных, таким образом: "Я не знаю длины, но существует один".

Однако во многих случаях это слишком много. Чтобы сделать что-то с экзистенциально квантованным типом данных, вам необходимо быть общим: например. если вам нужно найти сумму элементов SomeVec, вы должны определить sumVec для Vec с произвольной длиной. Затем разверните SomeVec и примените sumVec, сказав: "Я не знаю длины обернутого Vec, но sumVec не заботится". Но вместо этого wrapping-unwrapping вы можете напрямую использовать CPS.

Однако из-за этой общности вам нужно включить ImpredicativeTypes, чтобы иметь возможность определять, скажем, список с такими продолжениями. В этом случае список с экзистенциально квантифицированными данными является общим шаблоном, который позволяет обойти ImpredicativeTypes.

Что касается вашего примера, с правильно определенными Nat s, версия, которая сравнивает Nat, lazier, чем версия, которая сравнивает Integer s.

Ответ 2

Основное использование someNatVal - это использование значения во время выполнения, как если бы это был тип, который не был известен во время компиляции.

Мой ответ на Могу ли я найти неизвестный KnownNat?, является действительно тупым примером. Есть миллион лучших способов написать программу, которая делает то же самое, что и она. Но это показывает, что делает someNatVal. Это, по существу, функция от значения к типу - с экзистенциальным ограничением объема этого типа в местах, где он не статически известен.