Как я могу создать экземпляр `Functor` для типа с двумя параметрами? - PullRequest
4 голосов
/ 23 октября 2019

Фон. В одном из моих занятий мы изучали монаду Parser. Монада Parser обычно определяется либо как

newtype Parser a = Parser (String -> [(a, String)])

, либо как

newtype Parser a = Parser (String -> Maybe (a, String))

В любом случае мы можем создать экземпляр Parser как Functor, используя код, который работает воба случая:

instance Functor Parser where
  fmap f (Parser p) = Parser (fmap applyF . p)
    where applyF (result, s) = (f result, s)

Если у нас есть Parser, построенный для возврата Maybe (a, String), это применяется f к result, если существует result. И если у нас есть Parser, построенный для возврата [(a, String)], это применяется f к каждому из result, возвращенных в списке.

Мы можем создать экземпляр Applicative, Monad, MonadPlus и Alternative аналогичным образом (чтобы они работали либо для Maybe, либо []).

Вопрос. Если я параметризирую Parser в зависимости от типа, используемого для переноса результата, как я могу создать экземпляр для Functor и друзей?

newtype Parser m a = Parser (String -> m (a, String))

-- How do I instance `Parser` as a Functor if `m` is a Functor?

1 Ответ

9 голосов
/ 23 октября 2019

Здесь можно построить ограничение, согласно которому m должен быть типом, который также является экземпляром Functor, а затем fmap для этого результата:

instance <b>Functor m</b> => Functor (Parser m) where
    fmap f (Parser p) = Parser (\x -> <b>fmap g</b> (p x))
        where <b>g</b> (r, s) = (f r, s)

Здесь gтаким образом, функция, которая выполняет отображение f на первый элемент кортежа. Таким образом, мы используем это g как «функцию отображения» для результата.

Таким образом, это будет работать для любого m, который является экземпляром Functor, например, Maybe, [], Tree и т. д.

...