Линзы 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
не работает для всех Functor
s.
давайте посмотрим на ваши типы:
λ :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
работает для всех Functor
s.
но traverse
работает только для подмножества Functor
s, которые также Applicative
. Итак, таким образом, тип traverse
is больше специфический не допускается Lens
es.
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
, но я не уверен, что это именно то, что вы хотели бы хочу добиться здесь.