Почему "чисто" требуется только для аппликативных, а не для 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 является отображением, которое:

  • связывает каждый объект X ∈ C объект F (X) ∈ D.
  • связывает каждый морфизм f: X → Y ∈ C морфизм F (f): F (X) → F (Y) ∈ D такой, что выполняются следующие условия:

    • F (id: X → X) = id: F (X) → F (X) для любого объекта X ∈ C.
    • F (g ∘ f) = F (g) ∘ F (f) для всех морфизмов f: X → Y и g: Y → Z.

Категория состоит из объектов и морфизмов между объектами.

Весь код в Haskell входит в Hask, категорию Haskell. В Hask:

  • Типы - это объекты.
  • Функции - это морфизмы между типами.

Следовательно, все экземпляры Functor в Haskell являются функторами от Hask до Hask (т.е. они являются endofunctors).

Чтобы сделать это более строго, для всех экземпляров Functor в Haskell:

  • C = Hask.
  • D = Hask.

Теперь каждый функтор 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 соответственно.