($) относится к (.), как и `fmap`?

У меня есть функция funcM :: a -> b -> c -> IO (x, y)

Я хочу написать функцию funcM_ :: a-> b-> c-> IO x так:

funcM_ = fst `fmap` funcM -- error

Я мог бы добавить обратно все точки, но кажется, что должно быть что-то, на что я мог бы заменить fmap, чтобы вышеизложенное работало. Вроде как замена ($) на (.) заставит это работать в чистом контексте.

Какую функцию я ищу?


person John F. Miller    schedule 07.01.2014    source источник


Ответы (3)


Поскольку вы составляете функцию с одним аргументом (fmap) с функцией с тремя аргументами (funcM), вам нужно три уровня композиции:

funcM_ = ((fmap fst .) .) . funcM

Это эквивалентно указанной версии простым расширением:

funcM_ x = (fmap fst .) . funcM x
funcM_ x y = fmap fst . funcM x y
funcM_ x y z = fmap fst (funcM x y z)

Это действительно следует из типа fmap:

fmap :: (Functor f) => (a -> b) -> f a -> f b

Вы просто частично применяете аргументы к funcM, так что у вас есть f a (здесь IO (x, y)), которые вы передаете fmap fst, чтобы получить обратно f b (IO x).

Кроме того, M_ обычно подразумевает возврат m ().

person Jon Purdy    schedule 07.01.2014
comment
И на самом деле это return() Чтобы не перечислять много кода, я изменил рассматриваемую монаду с StateT Context (ErrorT Err STM) a на IO. Мне все еще нужно состояние, но мне не нужно значение. Аргументы a и b фактически используются для создания состояния, а значение c реализует восстанавливаемые ошибки. Все детали, которые не имеют значения для вопроса. Да, похоже, мне тоже нужен snd, а не fst. но это не имеет значения для заданного вопроса. - person John F. Miller; 07.01.2014

Взгляните на следующий ответ: https://stackoverflow.com/a/20279307/783743 В нем объясняется, как преобразовать ваш код в безточечный стиль. Начнем с небесточечного определения funcM_:

funcM_ a b c = fmap fst (funcM a b c)

-- But `\x -> f (g x)` is `f . g`. Hence:

funcM_ a b = fmap fst . (funcM a b)

-- But `\x -> f (g x)` is `f . g`. Hence:

funcM_ a = (fmap fst .) . (funcM a)

-- But `\x -> f (g x)` is `f . g`. Hence:

funcM_ = ((fmap fst .) .) . funcM

Другой способ сделать это — использовать uncurry и curry следующим образом:

uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d
uncurry3 f (a, b, c) = f a b c

curry3 :: ((a, b, c) -> d) -> a -> b -> c -> d
curry3 f a b c = f (a, b, c)

(.::) :: (d -> e) -> (a -> b -> c -> d) -> a -> b -> c -> e
f .:: g = curry3 (f . (uncurry3 g))

Теперь вы можете написать funcM_ следующим образом:

funcM_ = fmap fst .:: funcM

Вы также можете написать .:: в бесточечном стиле следующим образом:

(.::) :: (d -> e) -> (a -> b -> c -> d) -> a -> b -> c -> e
(.::) = (.) . (.) . (.)

Надеюсь, это помогло.

person Aadit M Shah    schedule 07.01.2014
comment
FYI (.::) определен в функторах: Data.Functor.Syntax hackage.haskell.org/package/functors-0.1/docs/ (полагаю, вы уже это знаете, иначе имя (.::) выглядит естественно привлекательным). - person misterbee; 07.01.2014
comment
Я думаю, вы имеете в виду funcM_ = fmap fst .:: funcM. - person Tom Ellis; 07.01.2014
comment
@TomEllis Да, действительно. Виноват. - person Aadit M Shah; 07.01.2014

Добавьте точку для каждого аргумента в funcM

Все они эквивалентны:

((fmap fst . ) .) . funcM
((.)  . (.)  . (.))  (fmap fst) funcM
(fmap . fmap . fmap) (fmap fst) funcM

import Data.Functor.Syntax -- from 'functors' package
(.::) (fmap fst) funcM

Обратите внимание, что все, что я сделал, это изменил неявное ($) на (.). :-)

(.) — это реализация fmap для экземпляра функции Functor:

instance Functor ((->) a) b where
  fmap f g = f . g

GHCi :t твой друг.

person misterbee    schedule 07.01.2014