Как думать об отсутствии законов - PullRequest
0 голосов
/ 24 июня 2018

Я математик, который много работает с теорией категорий, и я некоторое время использую Haskell для выполнения определенных вычислений и т. Д., Но я определенно не программист.Я действительно люблю Haskell и хочу стать более беглым в этом, и система типов - это то, что я считаю особенно полезным при написании программ.

Однако недавно я пытался реализовать категориютеоретические вещи, и я сталкиваюсь с проблемами, касающимися того факта, что у вас, по-видимому, не может быть законов о методах классов в Haskell.В случае, если моя терминология здесь неправильна, я имею в виду, что я могу написать

class Monoid c where
    id :: c -> c
    m :: c -> c -> c

, но я не могу написать какой-то закон в соответствии с

m (m x y) z ==  m x $ m y z

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

  1. Как мне изменить свой подход кHaskell для решения этой проблемы?Есть ли хорошее математическое / теоретико-типовое решение (например, требуется наличие ассоциатора, который является изоморфизмом (хотя тогда возникает вопрос, как мы кодируем изоморфизмы без закона?));есть ли хак (с использованием расширений, таких как DataKinds);я должен быть решительным и переключиться на использование чего-то вроде Идриса;или это лучший ответ, чтобы просто изменить то, как я думаю об использовании Haskell (т.е. признать, что эти законы не могут быть реализованы способом Haskelly)?
  2. (бонус) Как именно происходит отсутствие законовне поддерживает зависимые типы?

1 Ответ

0 голосов
/ 24 июня 2018

[обратите внимание, что переполнение стека, к сожалению, не поддерживает mathjax, но, как вы, кажется, уже получили степень по математике, я предполагаю, что вы можете прочитать ее]

Вы хотите, чтобы:

m (m x y) z = m x (m y z)        -- (1)

Но для этого вам нужен способ проверить это. Таким образом, вы или ваш компилятор (или помощник по проверке) должны создать доказательство этого. И вопрос в том, какой тип является доказательством (1)?

Можно представить себе некоторый тип Proof, но тогда, возможно, вы могли бы просто построить доказательство того, что 0 = 0 вместо доказательства (1) и оба будут иметь тип Proof. Так что вам нужен более общий тип. Я не могу решить, как разбить остальную часть вопроса, поэтому я пойду за кратким объяснением изоморфизма Карри-Ховарда, за которым последует объяснение того, как доказать, что две вещи равны, и затем, как зависимые типы релевантны.


Изоморфизм Карри-Говарда говорит, что пропозиции изоморфны типам, а доказательства изоморфны программам: тип соответствует предложению, а доказательство этого предложения соответствует программе, создающей значение, населяющее этот тип. Игнорируя, сколько может быть выражено в виде типов, примером может быть то, что тип A * B (записан (A, B) на Хаскеле) соответствует предложению «A и B», тогда как тип A + B (написано Either A B в Хаскеле) соответствует предложению «A или B». Наконец, тип A -> B соответствует «A подразумевает B», поскольку доказательством этого является программа, которая принимает доказательства A и дает вам свидетельство B. Следует отметить, что нет способа выразить not A, но можно представить добавление типа Not A со встроенными типами Either a (Not a) для закона исключенного среднего, а также Not (Not a) -> a и a * Not a -> Void (где Void - это тип, который не может быть обитаемым и, следовательно, соответствует false), но тогда невозможно действительно запустить эти программы, чтобы получить конструктивистские доказательства.

Теперь мы проигнорируем некоторые реалии Хаскелла и представим, что не существует способов обойти эти правила (в частности undefined :: a говорит, что все верно, а unsafeCoerce :: a -> b говорит, что что-либо подразумевает что-то еще, или просто другие функции, которые не не возвращайтесь туда, где их существование не подразумевает соответствующего доказательства).

Итак, мы знаем, как объединять предложения, но каким может быть предложение? Ну, можно сказать, что два типа равны. В Haskell это соответствует GADT

data Eq a b where Refl :: Eq c c

Где этот конструктор соответствует рефлексивному свойству равенства.

[примечание: если вы все еще заинтересованы, вам может быть интересно посмотреть на однолистные основы Воеводского в зависимости от того, насколько вас интересует идея «теории гомотопического типа»]

Так что мы можем доказать что-то сейчас? Как насчет переходного свойства равенства:

trans :: Eq a b -> Eq b c -> Eq a c
trans x y =
  case x of
  Refl -> -- by this match being successful, the compiler now knows that a = b
    case y of
    Refl -> -- and now b = c and so the compiler knows a = c
      Refl -- the compiler knows that this is of type Eq d d, and as it knows a = c, this typechecks as Eq a c

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


Итак, как бы вы могли доказать исходное утверждение (1)? Хорошо, давайте представим, что мы хотим, чтобы тип c был моноидом, тогда мы также должны доказать, что $ \ forall x, y, z: c, m (mxy) z = mx (myz). $ Итак, нам нужен способ выразить m (m x y) z как тип. Строго говоря, это не зависимые типы (это можно сделать с помощью DataKinds для продвижения значений и семейств типов вместо функций). Но вам нужны зависимые типы, чтобы типы зависели от значений. В частности, если у вас есть тип Nat натуральных чисел и семейство типов Vec :: Nat -> * (* - это тип (тип чтения) всех типов) векторов фиксированной длины, можно определить функцию зависимых типов mkVec :: (n::Nat) -> Vec n. Обратите внимание, как тип вывода зависит от значения входа.

Таким образом, ваш закон должен иметь функции, переведенные на уровень типов (пропуская вопросы о том, как определить равенство типов и равенство значений), а также зависимые типы (составленный синтаксис):

class Monoid c where
  e :: c
  (*) :: c -> c -> c
  idl :: (x::c) -> Eq x (e * x)
  idr :: (x::c) -> Eq x (x * e)
  assoc :: (x::c) -> (y::c) -> (z::c) -> Eq ((x * y) * z) (x * (y * z))

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


Заключительное замечание по теории зависимых типов и тому, как они соответствуют изоморфизму Карри Говарда.

Зависимые типы могутможно считать ответом на вопрос: какие типы соответствуют предложениям $ \ forall x \ in S \ quad P (x) $ и $ \ Существует y \ in T \ quad Q (y)? $

Ответ заключается в том, что вы создаете новые способы создания типов: зависимый продукт и зависимая сумма (побочный продукт).Зависимый продукт выражает «для всех значений $ x $ типа $ S, $ есть значение типа $ P (x). $». Нормальным продуктом будет зависимый продукт с $ S = 2, $ типом, населеннымдва значения.Зависимый продукт может быть написан (x:T) -> P x.Зависимая сумма говорит «некоторое значение $ y $ типа $ T $ в сочетании со значением типа $ Q (y). $», Это можно записать как (y:T) * Q y.

. Можно думать о них какобобщение произвольно проиндексированных (со) произведений из множества Set в общие категории, где можно разумно написать, например, $ \ prod_ \ Lambda X (\ lambda), $, а иногда такие обозначения используются в теории типов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...