两侧部分转换的光学器件
Optic for partial conversion on both sides
Prism
与 Iso
相似,除了两个转换之一是部分转换。是否有两种转换都是部分转换的光学器件?
当然可以创建类型 (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)
,然后放入 s
和 t
作为主要指数:
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
- 要从
PseudoIso
到 KIso
,请使用 dimapM
(PseudoIso
的字段是 dimapM
参数的正确类型)
- 从
KIso
到 PseudoIso
,部分申请身份 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编码。更简单。
Choice
是 Prism
的 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
层次结构可能类似于:充其量可能类似于
(Traversal
和 Lens
可以变成新的 Weird
光学元件吗?)
Lens
/ \
Iso Traversal -> Setter
\ /
Prism
\
Weird
让我们尝试另一个具体的 Profunctor
s。
我将使用 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
Prism
与 Iso
相似,除了两个转换之一是部分转换。是否有两种转换都是部分转换的光学器件?
当然可以创建类型 (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)
,然后放入 s
和 t
作为主要指数:
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
- 要从
PseudoIso
到KIso
,请使用dimapM
(PseudoIso
的字段是dimapM
参数的正确类型) - 从
KIso
到PseudoIso
,部分申请身份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编码。更简单。
Choice
是 Prism
的 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
.
层次结构可能类似于:充其量可能类似于
(Traversal
和 Lens
可以变成新的 Weird
光学元件吗?)
Lens
/ \
Iso Traversal -> Setter
\ /
Prism
\
Weird
让我们尝试另一个具体的 Profunctor
s。
我将使用 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