棱镜或仿射遍历的对偶是什么?

What is the dual of a prism or an affine traversal?

A 棱镜 是一种用于聚焦副产品类型的光学器件,而 affine traversal 是一种可以聚焦在 0 of 1 元素的光学器件,即 AffineTraversal s t a b 同构于 (s -> Maybe a, (s, b) -> t)。据我所知,如果使用适当的棱镜编码,当透镜由棱镜组成时,我们会得到仿射遍历。

我有兴趣将那个(天真的)公式中的 Maybe 移动到 setter 侧而不是 getter 侧,这样我就可以拥有一个始终只提取一个元素,但可能无法将其放回。

我的用例与优化类型有关。想象一下,我们有一个类型 A 及其细化 B (B ⊆ A)。然后是一个棱镜refined :: Prism' A B:一个A可能是也可能不是一个有效的B,但是每个B都可以re进入一个A.结合 Lens' C Arefined,我们有一个仿射遍历。在另一个方向,人们可以想象一个比 re refined 稍微聪明一点的光学器件 unrefined:一个 A 可以变成 Just b,如果它是一个有效的 BNothing,如果不是。现在,如果我们将 Lens' C Bunrefined 结合起来,我们就有了对偶仿射遍历:它总是可以从 C 获得 A,但放回任何旧的 A可能会违反 C 的不变量并产生 Nothing 而不是 Just c。更复杂的不变量可以用类似的方式保证。

有趣的是,Scala 的 monocle 库提供了细化类型的棱镜,但没有提供反向的棱镜。

我很难为这些 (s -> a, b -> Maybe t)(s -> a, (s, b) -> Maybe t) 小玩意想出规律,我想知道更抽象的光学公式是否有帮助。

我知道有了profunctor镜头,我们有

type Lens s t a b = forall p. Strong p => p a b -> p s t
type Prism s t a b = forall p. Choice p => p a b -> p s t
type AffineTraversal s t a b = forall p. (Strong p, Choice p) => p a b -> p s t

这非常清楚,透镜放大到乘积类型,棱镜放大到余积类型,仿射遍历能够放大到代数数据类型(乘积或余积,不少于此)。

答案是否与 Cochoice 甚至 Costrong 相关(从 profunctor 中删除乘积/副积而不是引入它)?我无法从他们那里恢复天真的公式,但是...

这是一半的答案,显示了 Cochoice 光学元件和 (s -> a, b -> Maybe t) 之间的对应关系。

{-# LANGUAGE RankNTypes #-}

module P where

import Data.Profunctor
import Control.Monad

data P a b s t = P (s -> a) (b -> Maybe t)

instance Profunctor (P a b) where
  dimap f g (P u v) = P (u . f) (fmap g . v)

instance Cochoice (P a b) where
  unleft (P u v) = P (u . Left) (v >=> v') where
    v' (Left t) = Just t
    v' (Right _) = Nothing

type Coprism s t a b = forall p. Cochoice p => p a b -> p s t

type ACoprism s t a b = P a b a b -> P a b s t

fromCoprism :: ACoprism s t a b -> P a b s t
fromCoprism p = p (P id Just)

toCoprism :: P a a s t -> Coprism s t a a
toCoprism (P u v) = unleft . dimap f g where
  f (Left s) = u s
  f (Right a) = a
  g b = case v b of
    Nothing -> Right b
    Just t -> Left t