Ответ 1
Да, есть Functor
без экземпляра Apply
. Рассмотрим сумму двух функций (которые являются показателями в алгебраических типах данных):
data EitherExp i j a
= ToTheI (i -> a)
| ToTheJ (j -> a)
Существует экземпляр Functor
для всех i
и j
:
instance Functor (EitherExp i j) where
fmap f (ToTheI g) = ToTheI (f . g)
fmap f (ToTheJ g) = ToTheJ (f . g)
но нет экземпляра Apply
для всех i
и j
сек
instance Apply (EitherExp i j) where
...
ToTheI f <.> ToTheJ x = ____
Невозможно заполнить пробел ____
i -> b
или j -> b
, когда у вас есть только f :: i -> a -> b
и x :: j -> a
. Для этого нам нужно что-то знать о i
и j
, но нет способа заглянуть внутрь каждого типа i
или j
в Haskell. Интуиция отвергает этот ответ; если вам что-нибудь известно о i
или j
, например, что они населены одним значением, то вы можете написать экземпляр Apply
для EitherExp
class Inhabited a where
something :: a
instance (Inhabited i, Inhabited j) => Apply (EitherExp i j) where
...
ToTheI f <.> ToTheJ x = ToTheI (const ((f something) (x something)))
Но мы не знаем, что каждый i
и каждый j
- это Inhabited
. Тип Void
ничем не населён. У нас даже нет способа узнать, что каждый тип является либо Inhabited
, либо Void
.
Наша интуиция на самом деле очень хорошая; когда мы можем проверить, как создаются типы, для алгебраических типов данных нет Functor
, у которого нет экземпляров Apply
. Ниже приведены два ответа, которые могут быть более приятными для нашей интуиции.
Нет...
... для алгебраических типов данных. Есть 3 варианта. Структура void, структура может быть пустой или структура не может быть пустой. Если структура недействительна, то она absurd
является Apply
. Если он может быть пустым, выберите любой пустой экземпляр и возвращайте его постоянно для любого применения. Если он не может быть пустым, то если это сумма структур, каждая из которых не может быть пустой, то можно применить законопослушное применение, применив одно из значений † от первого к одному из значений второго и возвращая его в некоторой постоянной структуре.
Применяемый закон очень слабый. Применить не нужно, чтобы иметь какой-либо смысл. Это не должно быть "zip-y". Это не обязательно должен быть fmap
в сочетании с подозрительными вещами, такими как pure
из Applicative
; в pure
нет понятия, с помощью которого можно написать закон, требующий, чтобы он имел смысл.
Когда структура может быть пустой
Выберите любой пустой экземпляр и возвращайте его постоянно для любого применения
u <.> v = empty
Proof
(.) <$> u <.> v <.> w = u <.> (v <.> w)
(((.) <$> u) <.> v) <.> w = u <.> (v <.> w) -- by infixl4 <$>, infixl4 <.>
(_ ) <.> w = u <.> (_ ) -- by substitution
empty = empty -- by definition of <.>
Когда структура не может быть пустой
Если структура f
не может быть пустой, существует функция extract :: forall a. f a -> a
. Выберите другую функцию c :: forall a. a -> f a
, которая всегда создает одну и ту же непустую структуру, всюду заполненную аргументом, и определите:
u <.> v = c (extract u $ extract v)
со свободными теоремами
extract (f <$> u) = f (extract u)
extract . c = id
Proof
(.) <$> u <.> v <.> w = u <.> (v <.> w)
(((.) <$> u) <.> v) <.> w = u <.> (v <.> w) -- by infixl4 <$>, infixl4 <.>
(c (extract ((.) <$> u) $ extract v)) <.> w = u <.> (v <.> w) -- by definition
(c ((.) (extract u) $ extract v)) <.> w = u <.> (v <.> w) -- by free theorem
c (extract (c ((.) (extract u) $ extract v)) $ extract w) = u <.> (v <.> w) -- by definition
c ( ((.) (extract u) $ extract v) $ extract w) = u <.> (v <.> w) -- by extract . c = id
c (((.) (extract u) $ extract v) $ extract w) = u <.> c (extract v $ extract w) -- by definition
c (((.) (extract u) $ extract v) $ extract w) = c (extract u $ extract (c (extract v $ extract w))) -- by definition
c (((.) (extract u) $ extract v) $ extract w) = c (extract u $ (extract v $ extract w) ) -- by extract . c = id
let u' = extract u
v' = extract v
w' = extract w
c (((.) u' $ v') $ w') = c (u' $ (v' $ w'))
c ((u' . v') $ w') = c (u' $ (v' $ w')) -- by definition of partial application of operators
c (u' $ (v' $ w')) = c (u' $ (v' $ w')) -- by definition of (.)
Еще немного стоит сказать об определении extract
для экспоненциальных типов, функций. Для функции i -> a
есть две возможности. Либо i
обитаем, либо нет. Если оно обитаемо, выберите какого-нибудь жителя i
† и определите
extract f = f i
Если i
необитаем (недействителен), то i -> a
- это тип устройства с единственным значением absurd
. Void -> a
- это просто еще один сложный пустой тип, который не содержит a
s; рассматривайте это как структуру, которая может быть пустой.
Когда структура пуста
Когда структура пуста, нет способов ее построить. Мы можем написать одну функцию из каждой возможной конструкции (нет ни одной, чтобы передать ее) для любого другого типа.
absurd :: Void -> a
absurd x = case x of {}
Пустые структуры могут быть Functor
с fmap f = absurd
. Точно так же они могут иметь экземпляр Apply
с
(<.>) = absurd
Мы можем тривиально доказать это для всех u
, v
и w
(.) <$> u <.> v <.> w = u <.> (v <.> w)
Нет u
, v
или w
, и требование неверно верно.
† С некоторыми предостережениями относительно принятия аксиомы выбора, чтобы выбрать индекс a
для экспоненциального типа a -> b
Да...
... для Хаскелла. Представьте себе другую базу Monad
, отличную от IO
, назовем ее OI
. Тогда Sum IO OI
является Functor
, но никогда не может быть Apply
.
... для реального мира. Если у вас есть машина, на которую вы можете отправлять функции (или стрелки в категории, отличной от Hask), но не можете объединить две машины вместе или извлечь их рабочее состояние, то они являются функтором без применения ,