Линзы 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, но я не уверен, что это именно то, что вы хотели бы хочу добиться здесь.