Почему "чисто" требуется только для аппликативных, а не для Functor?
Чтение this Wikibook о Основах теории хаскелов и теории категорий, я узнал о функторах:
Функтор - это, по существу, трансформация между категориями, поэтому данная категории C и D, функтор F: C → D
отображает любой объект A в C в F (A) в D.
отображает морфизмы f: A → B в C в F (f): F (A) → F (B) в D.
... Звучит все хорошо. Далее приведен пример:
Пусть также пример экземпляра:
instance Functor Maybe where
fmap f (Just x) = Just (f x)
fmap _ Nothing = Nothing
Здесь ключевая часть: конструктор типа Может быть, любой тип T new type, Maybe T. Кроме того, fmap ограничивается типами Maybe, которые принимают функция a → b для функции Maybe a → Maybe b. Но это! У нас определены две части, то, что принимает объекты в Hask для объектов в другая категория (что может быть типов и функций, определенных на Maybe типы) и что-то, что берет морфизмы в Хаске для морфизмов в этой категории. Так что, возможно, это функтор.
Я понимаю, как определение fmap
является ключевым. Я смущен тем, как "конструктор типа Maybe" предоставляет первую часть. Я бы предпочел что-то вроде pure
.
Если я правильно понял, Maybe
скорее сопоставляет C
с D
. (Таким образом, являясь морфизмом на уровне категории, который может быть требованием для Functor)
Я думаю, вы могли бы перефразировать мой вопрос следующим образом: есть ли Functor, который не имеет очевидной реализации pure
?
Ответы
Ответ 1
Я думаю, что вы путаетесь между типами и значениями. Здесь определение функтора:
Пусть C и D - категории. Функтор F от C до D является отображением, которое:
Категория состоит из объектов и морфизмов между объектами.
Весь код в Haskell входит в Hask, категорию Haskell. В Hask:
- Типы - это объекты.
- Функции - это морфизмы между типами.
Следовательно, все экземпляры Functor
в Haskell являются функторами от Hask до Hask (т.е. они являются endofunctors).
Чтобы сделать это более строго, для всех экземпляров Functor
в Haskell:
Теперь каждый функтор F является отображением, которое сопоставляет каждому объекту X ∈ C объект F (X) ∈ D.
- Обратите внимание, что X и F (X) являются объектами C и D соответственно.
- Поскольку оба C и D являются Hask, оба X и F (X) являются типами, а не значениями.
- Таким образом, F: Тип → Тип или в Haskell
f : * -> *
.
В самом деле, именно так определяется класс типа Functor
в Haskell:
class Functor (f : * -> *) where
fmap :: (x -> y) -> (f x -> f y)
Здесь fmap
- вторая часть функтора. Это функция от значений до значений. Однако сам Functor
является конструктором типа (т.е. Отображением от типов к типам). По этой причине Maybe
является функтором, а []
является функтором, но Maybe Int
и [Int]
не являются функторами.
Обратите внимание, что pure
не образует первую часть определения функтора, потому что это отображение из экземпляра X в экземпляр F (X) (т.е. функция от значений до значений). Однако нам нужно отобразить из X в F (X) (т.е. Отображение от типов к типам).
Ответ 2
Если я получу это правильно, Maybe
скорее сопоставляет C
с D
. (Таким образом, являясь морфизмом на уровне категории, который может быть требованием для Functor)
Не так, как C
и D
есть категории, а не типы Haskell. A Functor
(т.е. Экземпляр класса типа, в отличие от функтора вообще) является отображением категории Hask (категория типов и функций Haskell) на Hask; то есть C
и D
в этом случае Хаск. В главе Wikibook упоминается, что в разделе "Функторы на Хаске". В вашем примере конструктор типа Maybe
предоставляет первую часть отображения, беря некоторый тип a
(объект Hask) в тип Maybe a
(другой объект в Hask).
Я думаю, вы могли бы перефразировать мой вопрос следующим образом: Есть ли Functor
, который не имеет очевидной реализации pure
?
Одним из примеров является пара Functor
, (,) a
. fmap
легко записать - \f (x, y) -> (x, f y)
- но pure
и (<*>)
требуется ограничение Monoid
на a
, так как в противном случае не было бы возможности использовать дополнительные значения a
. Для более подробного обсуждения и других примеров см. Хорошие примеры не functor/functor/Applicative/Monad?
Ответ 3
Я бы сказал, что тип экземпляра Applicative
становится растяжкой для Either
(что было бы прекрасно, просто имея экземпляр для Bifunctor
, но с другой стороны, используя его как Monad, удобно), и будет (IMHO) неуместным для чего-то вроде:
data ABC a = A a | B a | C a
Где все A, B, C "одинаково хорошо". Поскольку нет очевидного выбора, для которого следует использовать для pure
, его вообще не следует предоставлять. Тем не менее, fmap
все еще отлично.
Ответ 4
Категория Hask имеет типы как объекты и функции как стрелки, поэтому сопоставление объектов, предоставляемое экземпляром Functor, должно отображать типы в типы.
fmap
отображает стрелки, т.е. отображает функции a -> b
на функции f a -> f b
для функтора f. Конструктор типа Functor - это отображение для объектов, то есть между типами.
Например, конструктор типа Maybe
сопоставляет тип t
с типом Maybe t
например. String
до Maybe String
.
Напротив, pure
отображает значения некоторого базового типа на значение соответствующего аппликативного типа, например. "abc" и (Just "abc" ) являются значениями String
и Maybe String
соответственно.