Какова цель контрамапа?
Я прочитал много статей о контрамапе и нашел https://hackage.haskell.org/package/contravariant-1.4.1/docs/Data-Functor-Contravariant.html#g:3- это самое лучшее.
В любом случае я нашел, как использовать, например:
*Lib Data.Functor.Contravariant> a = Predicate (x -> x > 20)
*Lib Data.Functor.Contravariant> :t contramap
contramap :: Contravariant f => (a -> b) -> f b -> f a
*Lib Data.Functor.Contravariant> :t contramap (x -> x * 2)
contramap (x -> x * 2) :: (Num b, Contravariant f) => f b -> f b
*Lib Data.Functor.Contravariant> :t contramap (x -> x * 2) a
contramap (x -> x * 2) a :: (Ord b, Num b) => Predicate b
*Lib Data.Functor.Contravariant> x = contramap (x -> x * 2) a
*Lib Data.Functor.Contravariant> getPredicate x 45
True
Но никак не мог сообразить, где это может пригодиться.
На веб-сайте, который я разместил выше, написано:
В то время как в Хаскелле можно думать о Функторе как содержащем или
производя значения, контравариантный функтор является функтор, который может быть
мыслится как потребляющая ценность.
Посмотрите на определение функтора:
class Functor (f :: * -> *) where
fmap :: (a -> b) -> f a -> f b
Он потребляет значение типа
a и производит значение типа b. На contramap он потребляет значение. class Contravariant (f :: * -> *) where
contramap :: (a -> b) -> f b -> f a
Какой тип переменной он использует a или b?
Также вопрос от https://www.fpcomplete.com/blog/2016/11/covariance-contravariance о положительной и отрицательной позиции . На сайте говорит:
- положительная позиция: переменная типа является
результат / выход / диапазон / кодомен функции - отрицательная позиция: переменная типа является аргументом / входом / областью
функция
Глядя на определение типа contramap:
contramap :: (a -> b) -> f b -> f a
На какую переменную типа указывает автор?
2 ответов:
Считается, что функтор
f"содержит" или "производит" значения в том смысле, чтоf aподобен контейнеруaзначений.fmapпозволяет преобразовать значения, содержащиеся вf.
Примеры:
[a]'содержит' неотрицательное число значений типаaIO aпотенциально делает некоторый IO и "возвращает" или "производит" значение типаa(->) r a'содержит' значение типаaдля каждого значенияrТеперь a
Contravariant fэто то, что может "принимать" или "потреблять" ценности.contramapпозволяет преобразовать вещьf a, Прежде чем она их поглотит.
Главным примером для этого обычно является использование чего-то вродеnewtype Op r a = Op { runOp :: a -> r }
(обратите внимание, чтоPredicate, который вы использовали, кажется, простоOp Bool)
Теперь у нас есть нечто, что может "потреблять" значения типа А. (аналогия может иметь больше смысла дляOp (IO ()))
Продолжая этот пример "потребления" ценностей, рассмотримo = Op (\x -> putStrLn x) :: Op (IO ()) String, теперь что, если мы хотим использоватьo, но для значений типаShow a => a? Ну вот для чего нуженcontramap!contramap show o :: Show a => Op (IO ()) a
(обратите внимание, что в этом простом случаеrunOp (contramap show o)простоПравка:
Еще одна интересная вещь, которую следует отметить оContravariant, касается ее составления.
ДаноContravariant c, Contravariant d, Functor fиnewtype Compose f g a = Compose { runCompose :: f (g a) }
у нас есть:
Compose f cтакжеContravariantcontramap f (Compose fca) = Compose $ fmap (contramap f) fcaCompose c fтакжеContravariantcontramap f (Compose cfa) = Compose $ contramap (fmap f) cfaCompose c dна самом деле являетсяFunctorfmap f (Compose cda) = Compose $ contramap (contramap f) cda
Это не совсем точно и является источником вашего замешательства. В частности, существуют три различных объекта в типеПосмотрите на определение функтора:
Он потребляет значение типаclass Functor (f :: * -> *) where fmap :: (a -> b) -> f a -> f baи производит значение типаb.fmap: функцияa -> b, "функциональное" значениеf aи" функциональное " значениеf b, и то, потребляется лиaили производится, зависит от этих объектов, так же как и то, является лиbпотребляется или производится.Мы можем распространить эти наблюдения на более крупные типы функций.
- функция типа
a -> bпотребляет иaи производитb. (Это предложение, которое вы сказали, но с новым добавлением, указывающим , к какому объекту оно применяется.)- Если
fявляетсяFunctor, то значение типаf a"производит"as.- Если
fявляетсяFunctor, то значение типаf b"производит"bs.
- Если
fявляетсяFunctor, тогда функция типаf a -> f bпотребляетf as и производитf bs; и поскольку функторы положительны, это в свою очередь означает, что функция потребляетas и производитbs.- Если
fявляетсяFunctor, то функция типа(a -> b) -> f a -> f bпотребляетa -> b- то есть в реализации мы будем производитьas, чтобы обеспечить функциюa -> bи потреблятьbs, которые она возвращает! Он делает это, чтобы произвестиf a -> f b, который потребляетas и производитbs.N. B. роль" потреблять "и" производить "чередуется довольно много в приведенном выше описании, даже в пределах одного объекта, который называется
fmap; так сказать,"fmapпотребляетas и производитbs " не говорит всей истории.Аналогично неточна и следующая формулировка:
В то время как в Хаскелле можно думать оFunctorкак о содержании или производстве значений, контравариантный функтор-это функтор, который можно рассматривать как потребляющий ценности.Вы интерпретировали это так, что
fmapкак объект (функция) содержит значения, в то время какcontramapпроизводит значения. Но это не то, что было задумано; вместо этого, утверждение должно было быть о самих (контравариантных) функториальных значениях, а не о (контра)отображениях, которые применяются к ним. Более точным утверждением является следующее:Что касается вашего вопроса о положительных и отрицательных позициях, вы можете насладиться некоторыми из моих предыдущих обсуждений этой темы здесь на SO.В то время как в Хаскелле можно думать о значении типа
f a(для которогоfявляетсяFunctor) Как содержащем или производя значенияa, контравариантный функторfпорождает значения типаf a, которые можно рассматривать как потребляющие значенияa.
Comments