两侧部分转换的光学器件

Optic for partial conversion on both sides

PrismIso 相似,除了两个转换之一是部分转换。是否有两种转换都是部分转换的光学器件?

当然可以创建类型 (s -> Maybe a, b -> Maybe t) 但我想知道这样的事情是否可以表示为 Optic _ _?

您可以通过替换 Hask 上的 profunctors(即 Profunctor,这是Iso 作为 Optic) 与 Kleisli 类别上的发音者(此处为 Maybe monad)。

class Monad m => KProfunctor m p where
  dimapM :: (s -> m a) -> (b -> m t) -> p a b -> p s t

-- dimapM pure pure = id
-- dimapM f g . dimapM h i = dimapM (h >=> f) (g >=> i)

type Optic p s t a b = p a b -> p s t
type KIso m s t a b = forall p. KProfunctor m p => Optic p s t a b

要构建这样的 profunctor 的一个示例,首先采用我们尝试使用的伪 isos 类型 (s -> m a, b -> m t),然后放入 st作为主要指数:

data PseudoIso m a b s t = MkPseudoIso
  { toM :: s -> m a
  , fromM :: b -> m t
  }

instance Monad m => KProfunctor m (PseudoIso m) where
  -- exercise for the reader
  • 要从 PseudoIsoKIso,请使用 dimapMPseudoIso 的字段是 dimapM 参数的正确类型)
  • KIsoPseudoIso,部分申请身份 PseudoIso a b a b

实际上,它不一定是 Kleisli 类别。任何类别 (:->) :: k -> k -> Type 的profunctor 都会给你 class 形式的光学 (s :-> a, b :-> t).

注意:您可以使用 KProfunctor Maybe 定义 Choice 的实例,因此也许所有内容都应该专门针对 Maybe,因此 Choice 可以合理地添加为KProfunctor 的 superclass,那么 KIso 将是 Prism.

的子类型

让我们看看profunctor编码。更简单。

ChoicePrism 的 class,我们正在制作 Prisms 的子 class,因此 Choice 是自然选择对于超级class:

class Choice p => Weird p where
    weird :: (s -> Maybe a) -> (b -> Maybe t) -> p a b -> p s t

如果我们尝试为 p = (->) 编写一个实例,那是行不通的。 所以我们的新型光学器件不是 Setter.

的超级class

层次结构可能类似于:充其量可能类似于 (TraversalLens 可以变成新的 Weird 光学元件吗?)

     Lens
   /     \
Iso       Traversal -> Setter
   \      /
     Prism
         \ 
           Weird

让我们尝试另一个具体的 Profunctors。 我将使用 my blog post: Glassery

中定义的类型

ForgetM用于实现preview

type Optic' p s a = p a a -> p s s

preview :: Optic' (ForgetM a) s a -> s -> Maybe a
preview o = runForgetM (o (ForgetM Just))

newtype ForgetM r a b = ForgetM { runForgetM :: a -> Maybe r }

instance Profunctor (ForgetM r) where
    dimap f _ (ForgetM p) = ForgetM (p . f)

instance Choice (ForgetM r) where
    right' (ForgetM p) = ForgetM (either (const Nothing) p)

instance Weird (ForgetM r) where
    weird sa _bt (ForgetM ab) = ForgetM $ \s -> sa s >>= ab

TaggedM 可用于定义相反方向的东西(不是 Glassery):

repreview :: Optic' TaggedM s a -> a -> Maybe s
repreview o a = unTaggedM (o (TaggedM (Just a)))

newtype TaggedM a b = TaggedM { unTaggedM :: Maybe b }

instance Profunctor TaggedM where
    dimap _sa bt (TaggedM b) = TaggedM (fmap bt b)

instance Choice TaggedM where
    right' (TaggedM b) = TaggedM (fmap Right b)

instance Weird TaggedM where
    weird _sa bt (TaggedM b) = TaggedM (b >>= bt)

我们现在可以试试这个了。

简单案例有效:

*Main> preview (weird Just Just) 'x'
Just 'x'
*Main> repreview (weird Just Just) 'x'
Just 'x'

棱镜可以用作新事物(事物right' = _Right):

*Main> preview right' (Left 'x')
Nothing
*Main> preview right' (Right 'x')
Just 'x'

还有一个很好的对称 Profunctor:

newtype Re p s t a b = Re { runRe :: p b a -> p t s }

instance Profunctor p => Profunctor (Re p s t) where
    dimap f g (Re p) = Re (p . dimap g f)

instance Cochoice p => Choice (Re p s t) where
    right' (Re p) = Re (p . unright)

instance Choice p => Cochoice (Re p s t) where
    unright (Re p) = Re (p . right')

我们可以为它编写Weird实例:

instance Weird p => Weird (Re p s t) where
    weird sa bt (Re p) = Re (p . weird bt sa)

我们注意到,我们需要添加 Cochoice 成为 Weird 的超级 class:

class (Choice p, Cochoice p) => Weird p where
    weird :: (s -> Maybe a) -> (b -> Maybe t) -> p a b -> p s t

这看起来很有希望。


范-拉尔霍芬。这很棘手。

比较 Prism profunctor 和 VL 编码:

type PrismVL s t a b = forall f p. (Choice p, Applicative f) => p a (f b) -> p s (f t)
type PrismP  s t a b = forall   p. (Choice p)                => p a    b  -> p s    t

好的开始是假设这个 Weird 看起来像

type WeirdOptic s t a b = forall p f. (Weird p, Applicative f) => Optic p f s t a b

也许我们也需要稍微加强 f 约束。

但我留下一些东西供您试验。 还有一个悬而未决的问题是这个新 Weird 光学器件背后的直觉是什么; 以及它应该有什么规律(成为光学的,而不仅仅是两个功能 一起砸了)。感觉比尝试做类似的 Monad / Kleisli 光学 任何比 Maybe 更好的东西都很难,但也许它也会成功。

这是使用 Filterable 类型-class:

lens 式光学解决方案(而不是其他答案中的 profunctor 光学)
-- A partial variant of (#) for partial reviews
infixr 8 #?
(#?) :: Optic Tagged Maybe s t a b -> b -> Maybe t
f #? b = Just b & Tagged & f & unTagged

-- A Prism "turned around", i.e a getter but a partial review
type InvPrism s t a b =
    forall p f. (Profunctor p, Filterable f) => Optic p f s t a b

-- A partial iso-morphism, i.e a partial getter and partial review
type PartialIso s t a b =
    forall p f. (Choice p, Applicative f, Filterable f) => Optic p f s t a b

-- Turn APrism around
invPrism :: APrism b a t s -> InvPrism s t a b
invPrism p =
    dimap
    (review (reviewing (clonePrism p)))
    (mapMaybe (^? getting (clonePrism p)))

-- Create a PartialIso from two partial conversions
partialIso :: (s -> Maybe a) -> (b -> Maybe t) -> PartialIso s t a b
partialIso sma ams =
    dimap
    (maybe (Left ()) Right . sma)
    (catMaybes . either (const (pure Nothing)) (fmap ams)) .
    right'

-- Coerce APrism to an Optic'
reviewing ::
    (Choice p, Bifunctor p, Functor f, Settable f) =>
    APrism s t a b -> Optic' p f t b
reviewing p =
    bimap f (fmap f)
    where
        f = runIdentity . unTagged . clonePrism p . Tagged . Identity