Семейства типов не могут возвращать тип RankN - обходные пути или альтернативы?

Я играю с расширяемой библиотекой записей и хочу написать функцию field который может работать как Lens или Traversal в зависимости от того, Symbol Ключ находится в списке ключей. Типовое семейство дается:

type family LensOrTraversal key keys s t a b where
    LensOrTraversal key '[] s t a b = 
        Traversal s t a b
    LensOrTraversal key (key =: val ': xs) s t a b = 
        Lens s t a b
    LensOrTraversal key (foo =: bar ': xs) s t a b = 
        LensOrTraversal key xs s t a b

Этот код дает мне ошибку:

/home/matt/Projects/hash-rekt/src/Data/HashRecord/Internal.hs:433:5: 
error:
    • Illegal polymorphic type: Traversal s t a b
    • In the equations for closed type family ‘LensOrTraversal’
      In the type family declaration for ‘LensOrTraversal’

В идеале я хотел бы иметь возможность повторно использовать field название для линз и обходов, так как это позволит вам написать

>>> let testMap = empty & field @"foo" .~ 'a'
>>> :t testMap
HashRecord '["foo" =: Char]
>>> testMap ^. field @"foo" 
'a'
>>> testMap ^. field @"bar"
Type error
>>> testMap ^? field @"bar"
Nothing

что следует за общим lens идиомы. Я могу предоставить fieldTraversal функция, которая делает то, что я хочу, но я бы предпочел перегрузить имя field если возможно. Как бы вы обошли это ограничение семейства типов?

1 ответ

Решение

Объектив уже пройден, только его квантификатор Rank2 не использует полное ограничение (оно просто требует Functor не Applicative).

type Lens s t a b      = ∀ f . Functor f     => (a -> f b) -> s -> f t
type Traversal s t a b = ∀ f . Applicative f => (a -> f b) -> s -> f t

Именно на уровне этого ограничения вы должны представить свое семейство типов:

import GHC.Exts (Constraint)
type family FieldOpticConstraint key keys :: (* -> *) -> Constraint where
  FieldOpticConstraint key '[] = Applicative
  FieldOpticConstraint key (key =: val ': xs) = Functor
  FieldOpticConstraint key (_ ': xs) = FieldOpticConstraint key xs

затем field не должен давать LensOrTraversal, но всегда пользовательская сигнатура Rank2 с ограничением, определяемым семейством типов.

Другие вопросы по тегам