Линзы Haskell: как сделать вид хорошо играть с траверсом?
Я пытаюсь узнать о линзах, реализуя его в Haskell. Я реализовал view комбинатора следующим образом:
{-# LANGUAGE RankNTypes #-}
import Control.Applicative
import Data.Traversable
type Lens s a = Functor f => (a -> f a) -> s -> f s
view :: Lens s a -> s -> a
view lens = getConst . lens Const
однако, когда я пытаюсь использовать его в сочетании с traverse Я получаю следующее сообщение об ошибке:
Prelude> :load Lens.hs
[1 of 1] Compiling Main ( Lens.hs, interpreted )
Ok, modules loaded: Main.
*Main> :t view traverse
<interactive>:1:6:
Could not deduce (Applicative f) arising from a use of ‘traverse’
from the context (Traversable t)
bound by the inferred type of it :: Traversable t => t a -> a
at Top level
or from (Functor f)
bound by a type expected by the context:
Functor f => (a -> f a) -> t a -> f (t a)
at <interactive>:1:1-13
Possible fix:
add (Applicative f) to the context of
a type expected by the context:
Functor f => (a -> f a) -> t a -> f (t a)
or the inferred type of it :: Traversable t => t a -> a
In the first argument of ‘view’, namely ‘traverse’
In the expression: view traverse
к сожалению, я не понимаю это сообщение об ошибке. Пожалуйста, объясните, что это значит и как я могу это исправить.
3 ответов
как уже объясняют другие ответы, проблема в том, что view рассчитывает на то, что работает для любого Functor f, а traverse работает только если f тоже Applicative (и есть функторы, которые не Аппл).
на lens, проблема решена путем создания типа view не взять Rank2 аргумент (на самом деле, большинство функций в объективе не используют синоним типа объектива, они всегда используют что-то более слабое). Для вашей функции, обратите внимание, что view только использует f ~ Const. Вот почему вы можете изменить подпись типа:
view :: ((a -> Const a a) -> s -> Const a s) -> s -> a
реализация может остаться прежней, но теперь view работает на traverse:
view traverse :: (Traversable t, Monoid a) => t a -> a
отметить лишнее Monoid ограничения. Это ограничение появляется, потому что если вы устанавливаете f ~ Const a на traverse :: (Traversable t, Applicative f) => (a -> f a) -> t a -> f (t a), вам нужен экземпляр Applicative (Const a). Этот экземпляр имеет Monoid ограничение a хотя. И это также имеет смысл, поскольку проходимым может быть пустым или содержать более одного элемент, так что вам нужно mempty и mappend.
tl; dr-согласно вашему определению Lens, a traverse не может быть Lens, потому что traverse не работает для всех Functors.
давайте посмотрим на ваши типы:
λ :set -XRankNTypes
λ :m +Control.Applicative Data.Traversable
λ type Lens s a = Functor f => (a -> f a) -> s -> f s
λ :t traverse
traverse
:: (Applicative f, Traversable t) => (a -> f b) -> t a -> f (t b)
теперь мы можем видеть, что traverse является, в некотором смысле, немного более общим, чем наш Lens type-он может принимать функцию
от a -> f b где наш объектив может принимать только функции a -> f a.
ограничение его в этом случае не проблема, поэтому мы можем сказать
λ :t traverse :: (Traversable t, Applicative f) => (a -> f a) -> t a -> f (t a)
traverse :: (Traversable t, Applicative f) => (a -> f a) -> t a -> f (t a)
:: (Applicative f, Traversable t) => (a -> f a) -> t a -> f (t a)
Итак, теперь очевидно, что если traverse должен быть Lens, должно быть Lens (t a) a, так как это единственный способ сделать переменные типа линейными.
так давайте попробуем это.
λ :t traverse :: Lens (t a) a
<interactive>:1:1:
Could not deduce (Traversable t1) arising from a use of `traverse'
from the context (Functor f)
bound by the inferred type of
it :: Functor f => (a -> f a) -> t a -> f (t a)
at Top level
or from (Functor f1)
bound by an expression type signature:
Functor f1 => (a1 -> f1 a1) -> t1 a1 -> f1 (t1 a1)
at <interactive>:1:1-24
Possible fix:
add (Traversable t1) to the context of
an expression type signature:
Functor f1 => (a1 -> f1 a1) -> t1 a1 -> f1 (t1 a1)
or the inferred type of
it :: Functor f => (a -> f a) -> t a -> f (t a)
In the expression: traverse :: Lens (t a) a
УФ, ему это не понравилось. О, подождите, чтобы использовать traverse наши типа t должен быть Traversable, так давайте добавим это ограничение. (Так же, как "возможное исправление") предлагает:
λ :t traverse :: Traversable t => Lens (t a) a
<interactive>:1:1:
Could not deduce (Applicative f1) arising from a use of `traverse'
from the context (Functor f, Traversable t)
bound by the inferred type of
it :: (Functor f, Traversable t) => (a -> f a) -> t a -> f (t a)
at Top level
or from (Traversable t1, Functor f1)
bound by an expression type signature:
(Traversable t1, Functor f1) =>
(a1 -> f1 a1) -> t1 a1 -> f1 (t1 a1)
at <interactive>:1:1-41
Possible fix:
add (Applicative f1) to the context of
an expression type signature:
(Traversable t1, Functor f1) =>
(a1 -> f1 a1) -> t1 a1 -> f1 (t1 a1)
or the inferred type of
it :: (Functor f, Traversable t) => (a -> f a) -> t a -> f (t a)
In the expression: traverse :: Traversable t => Lens (t a) a
хорошо, так что проблема в том, что он не может сделайте вывод, что f is Applicative (который также требуется использовать traverse), просто это Functor (которые он получает из определения Lens).
мы не можем добавить Applicative f к контексту, хотя -f скрыт. Когда мы говорим type Lens s a = Functor f => (a -> f a) -> s -> f s, мы говорим, что Lens работает для всех Functors.
но traverse работает только для подмножества Functors, которые также Applicative. Итак, таким образом, тип traverse is больше специфический не допускается Lenses.
traverse это типа:
traverse :: (Applicative f, Traversable t) => (x -> f y) -> t x -> f (t y)
если мы сделаем свободную переменную f в определении типа Lens явный, его определение на самом деле
type Lens s a = forall f . Functor f => (a -> f a) -> s -> f s
так view нужна функция, которая может работать на любой Functor, а traverse может работать только на Applicative.
вы можете исправить ошибку, просто изменив Functor на Applicative в определении Lens, но я не уверен, что это именно то, что вы хотели бы хочу добиться здесь.