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

Могу ли я составить список успехов с кратковременным провалом по составу аппликативных функторов?

Пользователь 'singpolyma' спросил reddit, если была какая-то общая структура, лежащая в основе:

data FailList a e = Done | Next a (FailList a e) | Fail e

Была предложена свободная монада, но я подумал, можно ли ее смоделировать в более общем плане с помощью аппликативных функторов. В Abstracting with Applicatives Базерман показывает нам, что сумма двух аппликативных функторов также является аппликативным функтором с смещением влево/вправо, если мы имеют естественное преобразование в направлении смещения. Это похоже на то, что нам нужно! Таким образом, я начал свое предложение, но потом быстро столкнулся с проблемами. Может ли кто-нибудь увидеть решения этих проблем?:


Во-первых, мы начнем с определения суммы двух функторов. Я начал здесь, потому что мы хотим моделировать типы сумм - либо успехи, либо успехи, и неудачу.

data Sum f g a = InL (f a) | InR (g a)

И два функтора, с которыми мы хотим работать:

data Success a = Success [a]
data Failure e a = Failure e [a]

Success прямолинейный - по существу Const [a]. Однако Failure e я не уверен. Это не аппликативный функтор, потому что pure не имеет никакого определения. Это, однако, экземпляр Apply:

instance Functor Success where
  fmap f (Success a) = Success a

instance Functor (Failure e) where
  fmap f (Failure e a) = Failure e a

instance Apply (Failure e) where
  (Failure e a) <.> (Failure _ b) = Failure e a

instance Apply Success where
  (Success a) <.> (Success b) = Success (a <> b)

instance Applicative Success where
  pure = const (Success [])
  a <*> b = a <.> b

Затем мы можем определить сумму этих функторов с естественным преобразованием справа налево (поэтому левое смещение):

instance (Apply f, Apply g, Applicative g, Natural g f) => Applicative (Sum f g) where
  pure x = InR $ pure x
  (InL f) <*> (InL x) = InL (f <*> x)
  (InR g) <*> (InR y) = InR (g <*> y)
  (InL f) <*> (InR x) = InL (f <.> eta x)
  (InR g) <*> (InL x) = InL (eta g <.> x)

И единственное, что нам теперь нужно сделать, это определить наше естественное преобразование, и именно здесь все рушится.

instance Natural Success (Failure e) where
  eta (Success a) = Failure ???? a

Невозможность создания Failure кажется проблемой. Кроме того, даже быть взломанным и использовать ⊥ не является вариантом, потому что это будет оценено, в случае, если у вас есть InR (Success ...) <*> InL (Failure ...).

Я чувствую, что у меня что-то не хватает, но я понятия не имею, что это такое.

Можно ли это сделать?

4b9b3361

Ответ 1

Я уверен, что "правильный" ответ состоит в том, чтобы сделать e моноидом, так как вам не понравилась идея обсуждения reddit.

Рассмотрим Failure "oops" [(*1),(*2),(*3)] <*> Failure "doh" [1,2,3] Должен ли результат "oops" или "doh" как сбой? Сделав e моноид, мы зафиксируем тот факт, что канонического выбора нет, и потребитель может выбрать свой яд (будь то First, Last, [] и т.д.)

Обратите внимание, что это решение, так же как и представление (Maybe e, [a]), неверно обрабатывает потоковые/потенциально-бесконечные данные, поскольку оно строго в том, имеет ли мы отказ, заканчивающийся списком.

В другой кодировке будут использоваться фиксированные точки аппликаций в соответствии с последующим сообщением (http://comonad.com/reader/2013/algebras-of-applicatives/).

Затем вы берете представленное там представление списка (FixF (ProductF Embed (Sum (Const ()))) a) и изменяете его, вставляя моноида ошибки в позицию устройства, чтобы получить следующее:

Monid mon => FixF (ProductF Embed (Sum (Const mon))) a

И обратите внимание, что вместо моноида вы можете использовать Maybe, чтобы получить ровно FailList, но, как и в случае с FailList, вы не получаете аппликативный экземпляр бесплатно, если только вы не пишете, указав право способ совместить ошибки.

Также обратите внимание, что при использовании фиксированного подхода, если у нас есть эквивалент Success [(*1),(*2),(*3)] <*> Failure "doh" [1,2,3,4,5], мы возвращаем a Success с тремя элементами (т.е. мы действительно нестримны в неудаче), в то время как в предложенном вами подходе мы возвращаем Failure с тремя элементами и ошибкой из списка с пятью списками. Таковы компромиссы потоковой передачи по сравнению с строгими.

Наконец, и наиболее просто, мы можем просто взять type FailList e = Product (Const (First e)) ZipList, чтобы использовать стандартные аппликативные механизмы и получить что-то очень близкое к исходному типу данных.

Ответ 2

{-# LANGUAGE FlexibleInstances #-}

instance Applicative (Sum Success (Failure e)) where
  pure x = InL $ pure x
  (InL f) <*> (InL x) = InL (f <*> x)
  (InR (Failure e fs)) <*> (InR (Failure _ gs)) = InR (Failure e (fs <*> gs))
  (InR (Failure e fs)) <*> (InL (Success gs)) = InR (Failure e (fs <*> gs))
  (InL (Success gs)) <*> (InR (Failure e fs)) = InR (Failure e (gs <*> fs))

Это потому, что вы всегда можете добавить сбой в список успехов;)

Вы также можете использовать этот тип класса вместо Natural f g:

class Transplant f g where
  transplant :: f a -> g b -> f b

instance Transplant (Failure e) Success where
  transplant (Failure e _) (Success a) = Failure e a

Не знаю, что значит категория-теория.