Как отобразить функцию на несколько аргументов конструктора, работающих с классом Monad в Haskell? - PullRequest
4 голосов
/ 05 января 2020

Проблема, с которой я столкнулся, связана с приложением >>= к типу образца, подобному этому:

data ThreeArgs a = ThreeArgs a a a deriving (Show,Eq)

instance Functor ThreeArgs where
    fmap f (ThreeArgs a b c) = ThreeArgs (f a) (f b) (f c)

instance Applicative ThreeArgs where
    pure x = ThreeArgs x x x
    (ThreeArgs a b c) <*> (ThreeArgs p s q) = ThreeArgs (a p) (b s) (c q)

Я бы объявил экземпляр Monad следующим образом:

instance Monad ThreeArgs where
    return x = ThreeArgs x x x
    (ThreeArgs a b c) >>= f = f ... -- a code I need to complete

Да, похоже, что f применяется ко всем трем аргументам ThreeArgs. Если я завершу последнюю строку

(ThreeArgs a b c) >>= f = f a

, то компилятор не будет иметь никаких претензий, тогда как результат:

*module1> let x = do { x <- ThreeArgs 1 2 3; y <- ThreeArgs 4 6 7; return $ x + y }
*module1> x
ThreeArgs 5 5 5 

это означает, что суммирование приводит к контексту с теми же значениями аргумента, хотя правильный вывод должен быть ThreeArgs 5 8 10. Как только я отредактировал в

(ThreeArgs a b c) >>= f = (f a) (f b) (f c)

предупреждения компилятора:

 Couldn't match expected type `ThreeArgs b
                                -> ThreeArgs b -> ThreeArgs b -> ThreeArgs b'
              with actual type `ThreeArgs b'

Итак, я вижу серьезную ошибку, направляющую мое понимание, но мне все еще довольно трудно понять класс monadi c и еще такие вещи в Haskell. Предположительно, я хочу использовать рекурсию здесь или что еще?

1 Ответ

7 голосов
/ 06 января 2020

ThreeArgs изоморфно c до ((->) Ordering). Свидетель:

to :: ThreeArgs a -> Ordering -> a
to (ThreeArgs x _ _) LT = x
to (ThreeArgs _ y _) EQ = y
to (ThreeArgs _ _ z) GT = z

from :: (Ordering -> a) -> ThreeArgs a
from f = ThreeArgs (f LT) (f EQ) (f GT)

Ваши экземпляры Functor и Applicative соответствуют принципам работы ((->) r), поэтому мы можем просто сделать так, чтобы его Monad one работал и мы закончили.

instance Monad ThreeArgs where
    ThreeArgs x y z >>= f = ThreeArgs x' y' z' where
        ThreeArgs x' _ _ = f x
        ThreeArgs _ y' _ = f y
        ThreeArgs _ _ z' = f z

Кстати, общий термин для таких структур данных, как ThreeArgs - это «представимый функтор», если вы хотите узнать об этом подробнее.

...