Существует ли какой-либо класс типов, определяющий функцию от `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)
.