Подтвердить что ты не робот

Насколько применимо действительно применение, а не "комбинирование"?

Для тип распространения неопределенности 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, который должен выполнить связанное ограничение?


* Линейные функции этого типа действительно имеют структуру векторного пространства, но я определенно не могу ограничиться ими.

4b9b3361

Ответ 1

Как я понимаю (как теоретик без категории), фундаментальная операция zip :: f a -> f b -> f (a, b) (сопоставление пары эффективных вычислений с эффектным вычислением, приводящим к паре).

Затем вы можете определить

  • fx <*> fy = uncurry ($) <$> zip fx fy
  • liftA2 g fx fy = uncurry g <$> zip fx fy

См. этот пост от Edward Yang, который я нашел