Где значения вписываются в категорию Hask?

Итак, у нас есть категория Hask, где:

  • типы являются объектами категории
  • функции-это морфизмы от объекта к объекту в категории.

аналогично Functor У нас есть:

  • конструктор типов как отображение объектов из одной категории в другую
  • fmap для отображения морфизмов из одной категории в другую.

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

  • каждый тип-это сама категория. Ex: Int-это категория всех целых чисел.
  • функции от значения к другому значению тот же тип есть морфизм категории. Например:Int -> Int
  • функции от одного значения к другому значению другой тип являются функтором для отображения значений одного типа в другой.

Теперь мой вопрос: Имеют ли значения смысл в категории Hask (или в общей теории категорий)? Если да, то любая ссылка, чтобы прочитать об этом, или если нет, то любая причина для этого.

надеюсь, вопрос имеет смысл:)

5 ответов


(я буду использовать слова с их значением из математики/теории категорий, а не программирования, если я mark it as code.)

категорий

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

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

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

один уровень ниже

каждый моноид-это категория с одним объектом. Давай воспользуемся этим.

как целые числа a категория?

на это есть более одного ответа (так как целые числа являются моноидом при сложении и моноидом при умножении). Давайте сделаем дополнение:

вы можете рассматривать целые числа как категорию с одним объектом, а морфизмы-такие функции, как (+1), (+2), (вычесть 4).

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

любой моноид создает категорию одного объекта так же, как мы только что сделали с целыми числами.

функторы из целых чисел?

функция f из целых чисел как категория под операцией +, к какому-то другому типу с операцией £ это формирует категорию, которая может быть функтором, только если у вас есть f(x+y) = f(x) £ f(y). (Это называется моноидом гомоморфизма). Большинство функций не являются морфизмами.

пример морфизма

Strings являются моноидом под ++, так что они категории.

len :: String -> Int
len = length

len является моноидом морфизм от String to Int, потому что len (xs ++ ys) = len xs + len ys, поэтому, если вы рассматриваете (String,++) и (Int,+) как категории len - это функтор.

пример не-морфизм

(Bool,||) является моноидом, с False как идентичность, так что это категория одного объекта. Функция

quiteLong :: String -> Bool
quiteLong xs = length xs > 10

не морфизм, потому что quiteLong "Hello " is False и quiteLong "there!" тоже False, а quiteLong ("Hello " ++ "there!") и True и False || False не True.

, потому что quiteLong это не морфизм, это также не функтор.

какова ваша точка зрения, Эндрю?

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

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

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

(обычно теория категорий входит в Haskell в виде функтора от Hask до Hask, поэтому на практике в функциональном программировании две категории одинаковы.)

так... каков именно ответ на первоначальный вопрос?

  • каждый тип-это сама категория. Ex: Int-это категория всех целых чисел.

если вы думаете о них в частности пути. Подробнее см. В ответе PhilipJF.

  • функции от значения к другому значению того же типа морфизм категории. Например:Int -> Int

я думаю, вы перепутали два уровня. Функции могут быть морфизмами в Hask, но не все функции Int -> Int являются функторами под структурой сложения, например f x = 2 * x + 10 не является функтором между Int и Int, поэтому это не морфизм категории (другой способ сказать функтор) от (Int,+) в (Int,+), но это морфизм Int -> Int в категории Hask.

  • функции от одного значения к другому значению другого типа являются функтором для отображения значений одного типа к другому.

нет, не все функции являются функторы, например quiteLong нет.

значения даже имеют смысл в категории Hask (или в общей категории теория)?

категории не имеют значений в теории категорий, они просто имеют объекты и морфизмы, которые рассматриваются как вершины и направленные ребра. Объекты не должны иметь значений, а значения не являются частью теории категорий.


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

устанавливает как скучные категории

одним из наиболее часто используемых инструментов в математике является "setoid" - то есть множество с отношением эквивалентности над ним. Мы можем думать об этом категорически через понятие "группоид". Ля группоид-это категория, где каждый морфизм имеет обратный такой, что f . (inv f) = id и (inv f) . f = id.

почему это захватывает идею отношения эквивалентности? Ну, отношение эквивалентности должно быть рефлексивным, но это просто категориальное утверждение, что оно имеет стрелки тождества, и оно должно быть транзитивным, но это просто композиция, наконец, оно должно быть симметричным (поэтому мы добавили инверсии).

обычное понятие равенства в математике на любом множестве таким образом порождает структуру groupoid: а именно тот, где единственными стрелками являются стрелки идентичности! Это часто называют "дискретной категорией".

оставлено в качестве упражнения для читателя, чтобы показать, что все функции функторы между дискретными категориями.

data Rational where
    Frac :: Integer -> Integer -> Rational
    SameRationa :: (a*d) ~ (b*c) -> (Frac a b) ~ (Frac c d)

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

Подходы Же

каждый тип в Haskell населен дополнительным значением, а именно undefined. Что здесь происходит? Ну, мы могли бы определить частичный порядок для каждого типа, связанного с тем, как "определено" значение, такое, что

forall a. undefined <= a

, но и вещи как

forall a a' b b'. (a <= a') /\ (b <= b') -> ((a,b) <= (a',b'))

Undefined менее определен в том, что он ссылается на значение, которое не завершается (на самом деле,undefined функция реализуется путем создания исключения в каждом haskell, но давайте притворимся, что это было undefined = undefined. Вы не можете быть уверены, что что-то не заканчивается. Если вам дают undefined все, что вы можете сделать, это подождать и посмотреть. Таким образом, это может быть ничего.

частичный порядок порождает категорию стандартным способом.

таким образом, каждый тип порождает категорию, в которой значения являются объектами таким образом.

почему функторы функции? Ну, функция не могу сказать он получил undefined из-за проблемы остановки. Таким образом, он либо должен вернуть undefined когда он сталкивается с одним, или он должен дать тот же ответ, независимо от того, что он был дан. Вам остается показать, что это действительно функтор.


в то время как есть некоторые другие, довольно замечательные, ответы здесь все они несколько пропускают ваш первый вопрос. Чтобы быть ясным, values просто не существует и нет смысла в категории Hask. Хаск не об этом хочет говорить.

вышесказанное кажется немного глупым, чтобы сказать или почувствовать, но я поднимаю его, потому что важно отметить, что теория категорий предоставляет только один объектив для изучения гораздо более сложных взаимодействий и структуры, доступные в чем-то столь же сложном, как язык программирования. Не стоит ожидать, что вся эта структура будет подчинена довольно простому понятию категории. [1]

другой способ сказать это, что мы пытаемся проанализировать сложную систему, и это иногда полезно посмотреть его как категорию для того, чтобы искать интересные модели. Именно это мышление позволяет нам ввести Hask, проверить, что он действительно образует категорию, заметить, что Maybe Кажется, ведет себя как функтор, а затем использует все эти механики для записи условий когерентности.

fmap id = id
fmap f . fmap g = fmap (f . g)

эти правила имели бы смысл независимо от того, вводим ли мы Hask, но видим


как техническая заметка, весь этот ответ предполагает, что Hask на самом деле "платонический" Hask, т. е. мы можем игнорировать дно (undefined и non-termination) столько, сколько нам нравится. Без этого почти весь спор разваливается на части.


Ну, один из ответов-более пристально взглянуть на то, что такое категориальный функтор. Явно, это сопоставление между двумя категориями (скажем, C и D) что приводит объекты C к объектам D и стрелки C к стрелкам D. стоит отметить, что в целом эти "отображения"s не являются категориальными стрелками-они просто формируют отношение между категориями и не обязательно разделяют структуру с категориями.

это важно, потому что даже учитывая Haskell FunctorС endofunctors в АСК, мы должны быть осторожны. В Hask объекты являются Haskell типы и стрелки-Haskell функции между этими типами.

давайте посмотрим на Maybe еще раз. Если это будет эндофунктор на Hask, нам нужен способ взять все типы в Хаск к другим типы in Hask. Это сопоставление не является функцией Haskell, хотя оно может выглядеть так:pure :: a -> Maybe a не квалифицируется, потому что он работает на стоимостью


есть несколько способов поставить вещи с точки зрения категорий. Специально языки программирования, которые оказываются очень богатыми конструкций.

Если мы выбираем категорию Hask, мы просто устанавливаем уровень абстракции. Уровень, на котором не так удобно говорить о ценностях.

однако константы могут быть смоделированы в Hask в виде стрелки от терминального объекта () до соответствующего типа. Затем, например:

  • True : () - > Боол
  • 'a' : () - > Char

вы можете проверить: Barr, Wells - Теория категорий для вычислений, раздел 2.2.


любая категория с терминальным объектом (или с терминальным объектом*s*) имеет так называемый глобальные элементы (или точки или константы, и Википедии, больше можно найти, например, в Awoday это книги по теории категорий см. 2.3 обобщенных элементов) объектов, которые мы можем назвать значения этих объектов здесь, принимая глобальные элементы как естественные и универсальные категориальное понятие для "ценностей".

например, Set имеет обычные элементы (множеств, объектов Set) как глобальные элементы, что означает, что элементы любого набора A можно рассматривать как разные функции (морфизмы из Set) {⋆} → A С подразделение {⋆} этой A. Для конечного множества A С |A| = n здесь n такие морфизмы, для пустого множества {} нет таких морфизмов {⋆} → {} на Set, так что {} "не имеет элементов" и |{}| = 0, для одноэлементных множеств {⋆} ≊ {+} однозначно, так что |{⋆}| = |{+}| = 1 и так далее. Элементы или" значения " наборов на самом деле являются просто функциями из одноэлементного набора (1, объект терминал в Set), так как существует изоморфизм A ≊ Hom(1, A) на Set (т. е. CCC, так что Hom внутренняя здесь и Hom(1, A) объект).

так что глобальные элементы являются обобщением этого понятия элементов в Set к любой категории с терминальные объекты. Его можно обобщить далее с помощью обобщенных элементов (в категории множеств, множеств или пространств морфизмы определяются действиями по точкам, но это не всегда так в общей категории). В общем, как только мы превратим "значения" (элементы, точки, константы, термины) в стрелки рассматриваемой категории, мы можем рассуждать о них с помощью язык этой конкретной категории.

аналогично, в Hask мы есть, например, true as ⊤ → Bool и false as ⊤ → Bool:

true :: () -> Bool
true = const True

false :: () -> Bool
false = const Frue

true ≠ false на обычный sence, кроме того, у нас есть семья как ⊤ → Bool (undefined, error "...", fix, общая рекурсия и так далее):

bottom1 :: () -> Bool
bottom1 = const undefined

bottom2 :: () -> Bool
bottom2 = const $ error "..."

bottom3 :: () -> Bool
bottom3 = const $ fix id

bottom4 :: () -> Bool
bottom4 = bottom4

bottom5 :: () -> Bool
bottom5 = const b where b = b

...

⊥ ≠ false ≠ true и это все, мы не можем найти никаких других морфизмов формы ⊤ → Bool, так что , false и true является единственным значением Bool который можно различить экстенсионально. Обратите внимание, что в Hask любой объект имеет значения, т. е. Обитаемый, так как всегда есть морфизмы ⊤ → A для любого типа A, это делает Hask отличается от Set или любой другой нетривиальной CCC (его внутренняя логика немного скучна, это то, что быстрое и свободное рассуждение морально правильно бумага о том, что нам нужно искать подмножество Haskell, которое имеет хороший CCC С здравой логикой).

кроме того, в теории типов значения синтаксически представлены в виде терминов, которые опять и аналогичная категориальная семантика.


а если говорить о " платоническом "(т. е.--52-->) Hask, тогда вот тривиальное доказательство A ≊ Hom(1, A) в Agda (который хорошо отражает эти платонические черты):

module Values where

open import Function
open import Data.Unit
open import Data.Product
open import Relation.Binary.PropositionalEquality

_≊_ : Set → Set → Set
A ≊ B = ∃ λ (f : A → B) → ∃ λ (f⁻¹ : B → A) → f⁻¹ ∘ f ≡ id × f ∘ f⁻¹ ≡ id

iso : ∀ {A} → A ≊ (⊤ → A)
iso = const , flip _$_ tt , refl , refl