Поместите два монадических значения в пару и верните его - PullRequest
4 голосов
/ 24 сентября 2011

Я играю с Parsec и хочу объединить два парсера в один с результатом, помещенным в пару, а затем передать ему другую функцию для обработки результата разбора, чтобы написать что-то вроде этого:

try (pFactor <&> (char '*' *> pTerm) `using` (*))

Итак, я написал это:

(<&>) :: (Monad m) => m a -> m b -> m (a, b)
pa <&> pb = do
  a <- pa
  b <- pb
  return (a, b)

И

using :: (Functor f) => f (a, b) -> (a -> b -> c) -> f c
p `using` f = (uncurry f) <$> p

Есть ли что-нибудь подобное (<&>), которое где-то было реализовано? Или это может быть написано бессмысленно? Я попытался fmap (,), но, кажется, трудно подобрать тип.

Ответы [ 5 ]

11 голосов
/ 25 сентября 2011

Лучше, чем <&> или liftM2 будет

(,) <$> a <*> b

, поскольку аппликативный стиль, кажется, набирает популярность и очень лаконичен. Использование аппликативного стиля для подобных вещей избавит от необходимости самого <&>, поскольку он намного понятнее, чем (,) <$> a <*> b.

Также это даже не требует монады - это будет работать и для Applicatives.

7 голосов
/ 25 сентября 2011

Есть ли что-нибудь подобное (<&>), которое где-то было реализовано? Или это может быть написано бессмысленно? Я попытался fmap (,), но, кажется, трудно подобрать тип.

Сейчас не знаю, реализовано ли это где-либо, но <&> должно быть таким же, как liftM2 (,). Отличие от fmap состоит в том, что liftM2 поднимает двоичную функцию в монаду.

6 голосов
/ 25 сентября 2011

Используя аппликативный стиль, нет необходимости помещать промежуточные результаты в кортеж, чтобы сразу применить незакрашенную функцию.Просто примените функцию «напрямую», используя <$> и <*>.

try ((*) <$> pFactor <*> (char '*' *> pTerm))

. В общем случае, если нормальные экземпляры Monad и Applicative,

do x0 <- m0
   x1 <- m1
   ...
   return $ f x0 x1 ...

эквивалентно

f <$> m0 <*> m1 <*> ...

за исключением того, что последняя форма является более общей и требует только Applicative экземпляра.(Все монады должны также быть аппликативными функторами, хотя язык не обеспечивает этого).

3 голосов
/ 25 сентября 2011

Обратите внимание, что если вы пойдете в противоположном направлении от Applicative, вы увидите, что способ, которым вы хотите объединить парсеры, хорошо вписывается в Arrow парадигму и парсерные стрелки реализацию.Например:

import Control.Arrow

(<&>) = (&&&) 

p `using` f = p >>^ uncurry f 
1 голос
/ 25 сентября 2011

Да, вы можете использовать аппликативный стиль, но я не верю, что это отвечает ни на один из ваших вопросов.

Есть ли какой-то уже определенный комбинатор, который берет две произвольные монады и вставляет их типы значений в пару, а затем вставляет эту пару в контекст?

Не входит ни в один из стандартных пакетов.

Можете ли вы сделать последний бит бесплатно?

Я не уверен, есть ли способ сделать функцию карри с арностью более 1 свободной. Я не думаю, что есть.

Надеюсь, что ответит на ваши вопросы.

...