Для тип распространения неопределенности Approximate
, я хотел бы иметь экземпляры для Functor
через Monad
. Это, однако, не работает, потому что мне нужна структура векторного пространства для содержащихся типов, поэтому на самом деле это должны быть ограниченные версии классов. Как там, кажется, нет стандартной библиотеки для тех (или есть? Пожалуйста, укажите мне. rmonad, но это использует *
, а не Constraint
как вид контекста, который кажется мне просто устаревшим), я написал мою собственную версию на время бытия.
Все работает легко для Functor
class CFunctor f where
type CFunctorCtxt f a :: Constraint
cfmap :: (CFunctorCtxt f a, CFunctorCtxt f b) => (a -> b) -> f a -> f b
instance CFunctor Approximate where
type CFunctorCtxt Approximate a = FScalarBasisSpace a
f `cfmap` Approximate v us = Approximate v' us'
where v' = f v
us' = ...
но прямой перевод Applicative
, например
class CFunctor f => CApplicative' f where
type CApplicative'Ctxt f a :: Constraint
cpure' :: (CApplicative'Ctxt f a) => a -> f a
(#<*>#) :: ( CApplicative'Ctxt f a
, CApplicative'Ctxt f (a->b)
, CApplicative'Ctxt f b) => f(a->b) -> f a -> f b
невозможно, так как функции a->b
не имеют необходимой структуры векторного пространства * FScalarBasisSpace
.
Однако работает работа по изменению определения ограниченного аппликативного класса:
class CFunctor f => CApplicative f where
type CApplicativeCtxt f a :: Constraint
cpure :: CAppFunctorCtxt f a => a -> f a
cliftA2 :: ( CAppFunctorCtxt f a
, CAppFunctorCtxt f b
, CAppFunctorCtxt f c ) => (a->b->c) -> f a -> f b -> f c
а затем определяя <*>#
, а не cliftA2
как свободную функцию
(<*>#) = cliftA2 ($)
вместо метода. Без ограничения, это полностью эквивалентное (на самом деле, много экземпляров Applicative
идут так или иначе), но в этом случае это действительно лучше: (<*>#)
все еще имеет ограничение a->b
, которое Approximate
не может выполнить, но это не повредит аппликативный экземпляр, и я все еще могу делать полезные вещи, например
ghci> cliftA2 (\x y -> (x+y)/x^2) (3±0.2) (5±0.3) :: Approximate Double
0.8888888888888888 +/- 0.10301238090045711
Я считаю, что ситуация будет по существу одинаковой для многих других применений CApplicative
, например примера Set
, который уже приведен в исходном блоке сообщение о типах ограничений.
Итак, мой вопрос:
является <*>
более фундаментальным, чем liftA2
?
Опять же, в безусловном случае они все равно эквивалентны. Я на самом деле нашел liftA2
более понятным, но в Haskell, скорее всего, более естественно думать о передаче "контейнеров функций", а не контейнеров объектов и некоторой "глобальной" операции для их объединения. И <*>
непосредственно индуцирует все liftAμ
для μ ε ℕ, а не только liftA2
; сделать это из liftA2
только действительно не работает.
Но тогда эти ограниченные классы, похоже, составляют точку для liftA2
. В частности, он позволяет экземплярам CApplicative
для всех CMonad
s, что не работает, когда <*>#
является базовым методом. И я думаю, мы все согласны с тем, что Applicative
всегда должен быть более общим, чем Monad
.
Что бы теоретики категории говорили обо всем этом? И есть ли способ получить общий liftAμ
без a->b
, который должен выполнить связанное ограничение?
* Линейные функции этого типа действительно имеют структуру векторного пространства, но я определенно не могу ограничиться ими.