`(a -> b) -> (c -> d)` в Haskell? - PullRequest
       20

`(a -> b) -> (c -> d)` в Haskell?

0 голосов
/ 21 декабря 2018

Это еще один вопрос теории теории Хаскелла.

Давайте возьмем что-то простое и известное в качестве примера.fmap?Так что fmap :: (a -> b) -> f a -> f b, исключая тот факт, что f на самом деле Functor.Насколько я понимаю, (a -> b) -> f a -> f b является ничем иным, как синтаксическим сахаром для (a -> b) -> (f a -> f b);отсюда вывод:

(1) fmap - это функция, производящая функцию.

Теперь, Hask также содержит функции, поэтому (a -> b) и, в частности, (f a -> f b) является объектом Hask (поскольку объекты Hask являются четко определенными типами Haskell - математическими наборами a-ka - и действительно существует множество типа (a -> b) для каждого возможного a, верно?).Итак, еще раз:

(2) (a -> b) - объект Hask.

Теперь происходит странная вещь: fmap, очевидно, является морфизм Hask, так что это функция, которая берет другую функцию и преобразовывает ее в еще одну функцию; final функция еще не была применена .

Следовательно, нужен еще один морфизм Хаск, чтобы перейти от (f a -> f b) к f b.Для каждого элемента i типа a существует морфизм apply_i :: (f a -> f b) -> f b, определяемый как \f -> f (lift i), где lift i - это способ построить f a с определенным i внутри.

Другой способ увидеть это в стиле GHC: (a -> b) -> f a -> f b.В отличие от того, что я написал выше, (a -> b) -> f a сопоставляется с обычным объектом Hask.Но такая точка зрения противоречит фундаментальной аксиоме Хаскелла - не многовариантные функции, а прикладные (карри) альтернативы.


Я хотел бы спросить на этом этапе: (a -> b) -> f a -> f b предполагается, что (a -> b) -> (f a -> f b) -> f b, подслащенный для простоты, или я что-то упускаю действительно, действительно важно там?

Ответы [ 4 ]

0 голосов
/ 23 декабря 2018

Ради полноты этого ответа основное внимание уделяется вопросу, который был рассмотрен в различных комментариях, но не другими ответами.

Другой способ увидеть его в стиле GHC:(a -> b) -> f a -> f b.В отличие от того, что я написал выше, (a -> b) -> f a сопоставляется с обычным объектом Hask.

-> в сигнатурах типа является ассоциативным справа.В таком случае (a -> b) -> f a -> f b на самом деле совпадает с (a -> b) -> (f a -> f b), и видеть в нем (a -> b) -> f a было бы синтаксической путаницей.Это ничем не отличается от того, как ...

(++) :: [a] -> [a] -> [a]

... не означает, что частичное применение (++) даст нам список [a] (скорее, это даст нам функцию, которая добавляет некоторыеlist).

С этой точки зрения вопросы теории категорий, которые вы поднимаете (например, о "необходимости [еще одного) морфизма Хаска, чтобы перейти от (f a -> f b) к f b")отдельный вопрос, хорошо решенный ответом Хорхе Адриано .

0 голосов
/ 21 декабря 2018

fmap на самом деле семейство морфизмов.Морфизм в Hask всегда от конкретного типа к другому конкретному типу.Вы можете думать о функции как о морфизме, если функция имеет конкретный тип аргумента и конкретный тип возвращаемого значения.Функция типа Int -> Int представляет морфизм (действительно, эндоморфизм) от Int до Int в Hask .fmap, однако имеет тип Functor f => (a -> b) -> f a -> f b.Не конкретный вид в поле зрения!У нас просто есть переменные типа и квазиоператор => для работы.

Рассмотрим следующий набор конкретных типов функций.

Int -> Int
Char -> Int
Int -> Char
Char -> Char

Далее рассмотрим следующие конструкторы типов

[]
Maybe

[], примененный к Int, возвращает тип, который мы можем назвать List-of-Ints, но мы обычно просто вызываем [Int].(Одна из самых запутанных вещей в функторах, когда я начинал, заключалась в том, что у нас просто нет отдельных имен для ссылки на типы, которые создают различные конструкторы типов; вывод просто именуется выражением, которое его оценивает.) Maybe Int возвращает тип, который мы только что назвали, ну, Maybe Int.

Теперь мы можем определить набор функций, подобных следующему

fmap_int_int_list :: (Int -> Int) -> [Int] -> [Int]
fmap_int_char_list :: (Int -> Char) -> [Int] -> [Char]
fmap_char_int_list :: (Char -> Int) -> [Char] -> [Int]
fmap_char_char_list :: (Char -> Char) -> [Char] -> [Char]
fmap_int_int_maybe :: (Int -> Int) -> Maybe Int -> Maybe Int
fmap_int_char_maybe :: (Int -> Char) -> Maybe Int -> Maybe Char
fmap_char_int_maybe:: (Char -> Int) -> Maybe Char -> Maybe Int
fmap_char_char_maybe :: (Char -> Char) -> Maybe Char -> Maybe Char

Каждая из них представляет собой отдельный морфизм в Hask , но когда мы определяем их в Haskell, много повторений.

fmap_int_int_list f xs = map f xs
fmap_int_char_list f xs = map f xs
fmap_char_int_list f xs = map f xs
fmap_char_char_list f xs = map f xs
fmap_int_int_maybe f x = case x of Nothing -> Nothing; Just y -> Just (f y)
fmap_int_char_maybe f x = case x of Nothing -> Nothing; Just y -> Just (f y)
fmap_char_int_maybe f x = case x of Nothing -> Nothing; Just y -> Just (f y)
fmap_char_char_maybe f x = case x of Nothing -> Nothing; Just y -> Just (f y)

Определения не отличаются, когда тип f отличается, только когда типx / xs отличается.Это означает, что мы можем определить следующие полиморфные функции

fmap_a_b_list f xs = map f xs
fmap_a_b_maybe f x = case x of Nothing -> Nothing; Just y -> Just (f y)

, каждая из которых представляет набор морфизмов в Hask .

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

С этим путем мы можем лучше понять fmap :: Functor f => (a -> b) -> f a -> f b.

Учитываяfmap f, мы сначала посмотрим на тип f.Например, мы можем узнать, что f :: Int -> Int, что означает, что fmap f должен вернуть один из fmap_int_int_list или fmap_int_int_maybe, но мы еще не уверены, какой именно.Поэтому вместо этого он возвращает ограниченную функцию типа Functor f => (Int -> Int) -> f Int -> f Int.Как только эта функция будет применена к значению типа [Int] или Maybe Int, у нас, наконец, будет достаточно информации, чтобы узнать, какой морфизм на самом деле имеется в виду.

0 голосов
/ 23 декабря 2018

Теперь происходит странная вещь: fmap, очевидно, является морфизмом Hask, поэтому это функция, которая берет другую функцию и преобразует ее в еще одну функцию;Функция final еще не была применена.

Следовательно, нужен еще один морфизм Хаск, чтобы перейти от (fa -> fb) к f b.Для каждого элемента i типа a существует морфизм apply_i :: (fa -> fb) -> fb, определенный как \ f -> f (lift i), где lift i - это способ построения fa с конкретным i внутри.

Понятие применения в теории категорий моделируется в виде CCC - декартовых закрытых категорий .Категория ? - это CCC, если у вас есть естественная биекция ? (X × Y, Z) ≅ ? (X, Y⇒Z).

В частности, это означает, что существует естественное преобразование ? (оценка), где ? [Y, Z] :( Y⇒Z) × Y → Z, такое что для каждого g: X × Y →Z существует ?g: X → (Y⇒Z) такое, что g = ?g × id; ? [Y, Z].Поэтому, когда вы говорите,

Следовательно, нужен еще один морфизм Хаска, чтобы перейти от (fa -> fb) к f b.

То, как вы переходите от (f a -> f b) к f b или, используя обозначения выше, из (f a ⇒ f b), осуществляется через ?[f a,f b]:(f a ⇒ f b) × f a → f b.

Другой важный момент, который следует иметь в виду, заключается в том, что в теории категорий «элементы» не являются примитивными понятиями.Скорее элемент - это стрелка вида ? → X, где ? - конечный объект.Если вы возьмете X = ?, у вас есть ? (Y, Z) ≅ ? (? × Y, Z) ≅ ? (?, Y⇒Z).То есть морфизмы g: Y → Z находятся в биекции к элементам ?g: ? → (Y⇒Z).

В Haskell это означает, что функции являются именно «элементами» типов стрелок.Таким образом, в Haskell приложение h y будет смоделировано с помощью вычисления ?h: ? → (Y⇒Z) для y: ? → Y.То есть оценка (?h) × y: ? → (Y⇒Z) × Y, которая дается композицией (?h) × y; ? [Y, Z]: ? → Z.

0 голосов
/ 21 декабря 2018

- это (a -> b) -> f a -> f b предположим, что это (a -> b) -> (f a -> f b) -> f b, подслащенный для простоты

Нет.Я думаю, что вы упускаете, и это не ваша вина, это то, что это только очень особый случай, когда среднюю стрелку в (a -> b) <b>-></b> (f a -> f b) можно назвать морфизм так же, как внешний (a <b>-></b> b) -> (f a <b>-></b> f b) Можно.Общий случай класса Functor будет (в псевдосинтаксисе)

class (Category (──>), Category (~>)) => Functor f (──>) (~>) where
  fmap :: (a ──> b) -> f a ~> f b

Таким образом, он отображает морфизмы в категории, стрелки которых обозначены ──>, в морфизмы в категории ~>, но этосамо отображение морфизма просто функция .Справа, в Hask , в частности, функциональные стрелки - это стрелки того же типа, что и стрелки морфизма, но это математически говорит о довольно вырожденном сценарии.

...