Возможно ли реализовать этот обобщенный c флип? - PullRequest
2 голосов
/ 04 февраля 2020

Я хотел бы написать объект с сигнатурой типа:

genericFlip ::
  ( MonadReader (o (n c)) m
  , MonadReader a n
  , MonadReader b o
  )
    => m (n (o c))

Это, по сути, переворот для читателей монад.

Теперь достаточно просто написать версию этого это выглядит так:

genericFlip ::
  ( MonadReader (b -> a -> c) m
  , MonadReader a n
  , MonadReader b o
  )
    => m (n (o c))
  genericFlip = do
    f <- ask
    return $ do
      a <- ask
      return $ do
        b <- ask
        return $ f b a

Или даже заменить (->) на Reader, но, как бы я ни ломал свой мозг, я не могу определить, что подходит для всех читателей.

Можно ли создать общий объект с такой сигнатурой типа в Haskell?

Ответы [ 3 ]

1 голос
/ 07 февраля 2020

Для начала предлагаю не обобщать m до MonadReader. В типе flip ...

flip :: (a -> b -> c) -> (b -> a -> c)

... средняя стрелка не похожа на другие: она просто связывает вход и выход щелчка. Принимая это упрощение, мы получаем более простой тип для genericFlip, который вы предлагаете:

genericFlip :: (MonadReader a n, MonadReader b o) => o (n c) -> n (o c)

В любом случае, genericFlip не может быть реализован с этой подписью. Сам по себе интерфейс MonadReader не предоставляет способ предоставить среду для вычислений, которая была бы необходима для обмена слоями. Возьмем, к примеру, специализированную genericFlip из вашего вопроса:

genericFlip' :: (MonadReader a n, MonadReader b o) => (b -> a -> c) -> n (o c)
genericFlip' f = do
  a <- ask
  return $ do
    b <- ask
    return $ f b a

Она основывается на том, что f является функцией, что означает, что мы можем предоставить ей среду (и, как вы заметили, мы использовали Reader вместо этого мы могли бы сделать то же самое через runReader). В конечном итоге все, что MonadReader делает здесь, это превращает функции в вычисления для читателей, что становится прозрачным благодаря этому бессмысленному написанию:

genericFlip' :: (MonadReader a n, MonadReader b o) => (b -> a -> c) -> n (o c)
genericFlip' = fmap reader . reader . flip

Одно обобщение flip, которое у нас есть, это distribute:

distribute :: (Distributive g, Functor f) => f (g a) -> g (f a)

distribute @((->) _) также известен как flap или (??), тогда как distribute @((->) _) @((->) _) равно flip.

Distributive, тем не менее, не вполне отвлекает нас от функций в том смысле, в котором мы могли бы надеяться в контексте этого вопроса. Каждый дистрибутивный функтор изоморфен от c до (->) r для некоторого специфика c r. Связи становятся более очевидными, когда мы смотрим на класс Representable, который в принципе эквивалентен Distributive, но использует более сложную кодировку, которая делает изоморфизм явным. В дополнение к distribute, являющемуся обобщением flip, мы имеем index в качестве аналога приложения-функции и tabulate, который очень похож на reader. Класс, по сути, предлагает реализацию по умолчанию MonadReader , которую можно легко получить с помощью Co newtype .

В заключение отметим, что что-то, что, несмотря на то, что мы не совсем точно подходим к нашим предлагаемым обобщенным перевернутым сигнатурам, очень легко переворачивается, это ReaderT r (ReaderT s m) a, которое сводится к r -> s -> m a. Возможно, это не так уж и полезно, хотя на практике, как правило, вместо того, чтобы иметь вложенные слои считывателей, объединить среды в один тип и иметь только один слой считывателей (см. Также RIO monad * 1064). *).

0 голосов
/ 04 февраля 2020

Просто полагаясь на MonadReader, вы не можете, но с Traversable вы можете получить это:

genericFlip :: (Traversable o, MonadReader (o (n c)) m, Monad n) => m (n (o c))
genericFlip = do
  onc <- ask
  return $ sequence onc

Добавление других MonadReader ограничений не принесет никакого вреда, но я могу ' Я не вижу точно, чего вы пытаетесь достичь с помощью этих монад или их окружения.

genericFlip ::
  (Traversable o, MonadReader (o (n c)) m, MonadReader a n, MonadReader b o) => m (n (o c))
genericFlip = asks sequence

Пример может помочь.

0 голосов
/ 04 февраля 2020

Нет. Это невозможно.

В первой подписи нет ничего, что делало бы проходимым.

...