Это помогает знать немного теории категорий. Категория — это просто набор объектов со стрелками между ними. Они могут моделировать многое в математике, но для наших целей нас интересует категория типа; Hask — это категория типов Haskell, где каждый тип является объектом в Hask, а каждая функция — стрелкой между типом аргумента и типом возвращаемого значения. Например, Int
, Char
, [Char]
и Bool
— это все объекты в Hask, а ord :: Char -> Int
, odd :: Int -> Bool
и repeat :: Char -> [Char]
могут быть примерами стрелок в Hask.
Каждая категория имеет несколько свойств:
Каждый объект имеет идентификационную стрелку.
Стрелки составляют, так что если a -> b
и b -> c
стрелки, то и a -> c
тоже.
Стрелки идентичности — это как левые, так и правые идентичности для композиции.
Композиция ассоциативна.
Причина, по которой Hask является категорией, заключается в том, что каждый тип имеет функцию идентификации, а функции составляют. То есть id :: Int -> Int
и id :: Char -> Char
являются стрелками-идентификаторами для категории, а odd . ord :: Char -> Bool
— составными стрелками.
(Не обращайте внимания на то, что мы думаем, что id
— это полиморфная функция с типом a -> a
, а не набор отдельных функций с конкретными типами. Это демонстрирует концепцию теории категорий, называемую естественным преобразованием, которая вам не нужна. думать теперь)
В теории категорий функтор F — это отображение между двумя категориями; он сопоставляет каждый объект одной категории с объектом другой, а также также сопоставляет каждую стрелку одной категории со стрелкой другой. Если a
является объектом одной категории, мы говорим, что F a является объектом другой категории. Мы также говорим, что если f — стрелка первой категории, то соответствующая стрелка другой категории, если F — f.
Не всякое отображение является функтором. Он должен подчиняться двум свойствам, которые должны выглядеть знакомыми.
- F должен сопоставить стрелку идентичности объекта a с стрелкой идентичности объекта F a.
- F должен сохранять композицию. Это означает, что композиция двух стрелок в первой категории должна быть сопоставлена с композицией соответствующих стрелок в другой категории. То есть, если
h = g ∘ f
находится в первой категории, то h
сопоставляется с F h = F g ∘ F f
в другой.
Наконец, эндофунктор — это специальное имя для функтора, который отображает одну категорию в саму себя. В Hask класс типов Functor
отражает идею эндофунктора от Hask до Hask. Сам конструктор типов отображает типы, а fmap
используется для отображения стрелок.
Возьмем, к примеру, Maybe
. Конструктор типа Maybe
является эндофунктором, поскольку он сопоставляет объекты в Hask (типы) с другими объектами в Hask (другие типы). (Этот момент немного скрыт, поскольку у нас нет новых имен для целевых типов, поэтому Maybe
можно представить как сопоставление Int
с типом Maybe Int
.)
Чтобы сопоставить стрелку a -> b
с Maybe a -> Maybe b
, мы предоставляем определение для fmap
в экземпляре Maybe Int
. Maybe
также отображает функции, но вместо этого использует имя fmap
. Законы функтора, которым он должен подчиняться, совпадают с двумя, перечисленными в определении функтора.
fmap id = id
(карты с id :: Int -> Int
по id :: Maybe Int -> Maybe Int
.
fmap f . fmap g = fmap f . g
(то есть fmap odd . fmap ord $ x
должно возвращать то же значение, что и fmap (odd . ord) $ x
для любого возможного значения x
типа Maybe Int
.
В качестве несвязанного тангенса другие указали, что некоторые вещи в Haskell не являются функциями, а именно литеральными значениями, такими как 4
и "hello"
. Хотя это верно для языка программирования (вы не можете, например, составить 4
с другой функцией, которая принимает Int
в качестве значения), верно то, что в теории категорий вы можете заменить значения функциями от типа единицы ()
к типу значения. То есть буквальное значение 4 можно рассматривать как стрелку 4 :: () -> Int
, которая при применении к (единственному) значению типа ()
возвращает значение типа Int
, соответствующее целому числу 4. Эта стрелка будет составляться, как и любая другая; odd . 4 :: () -> Bool
будет отображать значение из типа единицы измерения в логическое значение, указывающее, является ли целое число 4 нечетным или нет.
Математически это приятно. Нам не нужно определять какую-либо структуру для типов; они просто есть, и, поскольку у нас уже есть представление об определенном типе, нам не нужно отдельное определение того, что такое значение типа; мы просто определяем их с точки зрения функций. (Вы могли заметить, что нам по-прежнему нужно фактическое значение из типа единиц измерения. Возможно, в нашем определении есть способ избежать этого, но я недостаточно хорошо знаю теорию категорий, чтобы объяснить это так или иначе.)
Для фактической реализации нашего языка программирования подумайте о литеральных значениях как об оптимизации, позволяющей избежать концептуальных издержек и накладных расходов на производительность, связанных с необходимостью использовать 4 ()
вместо 4
каждый раз, когда нам просто нужно постоянное значение.
person
chepner
schedule
06.04.2016
Functor
являются конкретно эндофункторами категории Hask. - person leftaroundabout   schedule 06.04.2016