Существует ли какой-либо класс типов, определяющий функцию от `a -> mb` до `m (a → b)`?

Функция изa -> m bкm (a -> b)редко появляется в программировании, но может быть реализован в монаде Reader. Следующий код является предварительной реализацией. Существует ли такая библиотека?

      class Monad m => MonadShift m where
  shift :: (a -> m b) -> m (a -> b)

instance MonadShift Identity where
  shift f = Identity (\x -> runIdentity (f x))

instance MonadShift m => MonadShift (ReaderT r m) where
  shift f = ReaderT (\r -> shift (\x -> runReaderT (f x) r))

2 ответа

Это специализацияdistribute :: Functor f => f (g a) -> g (f a)принадлежащийDistributiveкласс, гдеfэто Функтор(->) b. Затем вы получаете сигнатуру типа:

      distribute :: (b -> g a) -> g (b -> a)

Обратите внимание, что (1) это не требуетgбытьMonad, но всего лишьFunctorи (2)IdentityиReaderTэкземпляры — это, по сути, единственные экземпляры, которые вы можете определить:

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

Этот вопрос дает прекрасную возможность собрать информацию об этой функции, которая разбросана по множеству вопросов и ответов здесь. Поскольку на самом деле у него нет стандартного имени, я просто назову его так же, как вы.

Как указывает Ноутмар , один из способов возникновения — это специализация :

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

shift :: Distributive g => (r -> g a) -> g (r -> a)
shift = distribute @_ @((->) _)

Если вместо этого мы специализируем другой функтор типа , до функтора функции, мы получаем комбинатор, известный по-разному как или (??):

      flap :: Functor f => f (s -> a) -> (s -> f a)
flap = distribute @((->) _)
-- flap m s = (\f -> f s) <$> m

(Если мы освоим обе специализации одновременно, в итоге получимflipиз Прелюдии.)

Сdistribute . distribute = id, и являются полными обратными:

      flap . shift = id
shift . flap = id

Дан дистрибутивный функторg, иflapпоэтому дайте нам изоморфизм между стрелками Клейслиa -> g bи статические стрелкиg (a -> b). Это отражает то, что монада и аппликативные экземпляры дистрибутивного функтора в конечном итоге эквивалентны. Такой факт легче проверить для функтора функции и распространяется на другие дистрибутивы черезRepresentableизоморфизм, на который ссылается ответ Ноутмара. (Кстати, все дистрибутивные функторы действительно являются монадами, хотя это и не очевидно из рассмотрения.Data.Distributiveучитывая, как в настоящее время устроен API указанного модуля.)

Наконец, хотя это специализация , можно изменить ситуацию и выразитьdistributeс точки зренияshift:

      distribute m = (\p -> p <$> m) <$> shift id

Мы можем подумать оshift id :: Distributive g => g (g a -> a)как проведение в каждой позиции дистрибутивного функтора формы ag a -> aэкстрактор для той позиции, которая привязана кpв определении выше. При этом отображение болееshift idпозволяет выбирать значения из совпадающих позиций внутриm :: f (g a).

Другие вопросы по тегам