我注意到,我有很多函数可以在我的值中添加某种标记。例如,请考虑以下两种数据类型:
data Named a = Named String a
data Colored a = White a | Black a
以及一些使用它们的功能:
name :: Foo -> Named Foo
color :: Named Foo -> Colored (Named Foo)
在某个时候,我开始拥有带有许多嵌套“标签”的函数,所以我想知道是否可以对此进行概括以使其更易于管理。所有这些都可能与PureScript的行多态性很好地兼容,但是我们在这里谈论Haskell。无论如何,这是我想出的:
class Tag f where
separate :: f a -> (forall b. b -> f b, a)
法律将类似于:
fx = let (f, x) = separate fx in f x
或没有类型检查但更优雅的版本:
uncurry ($) . separate = id
Tag
也可以成为Functor
的子类,条件是
fmap g fx = let (f, x) = separate fx in f (g x)
示例数据类型的实例如下:
instance Tag Named where
separate (Named name x) = (Named name, x)
instance Tag Colored where
separate (White x) = (White, x)
separate (Black x) = (Black, x)
...和其他一些一般实例:
instance Tag Identity where
separate (Identity x) = (Identity, x)
instance (Tag f, Tag g) => Tag (Compose f g) where
separate (Compose fgx) =
let (f, gx) = separate fgx in
let (g, x) = separate gx in
(Compose . f . g, x)
使整个类型类真正有用的是此函数:
reorder :: (Tag f, Tag g) => f (g a) -> g (f a)
reorder fgx =
let (f, gx) = separate fgx in
let (g, x) = separate gx in
g (f x)
它看起来像一些显而易见的习语,因此它必须为社区所熟知。当您不知道要搜索的事物的名称,而Hoogle却没有结果时,Google并不是很有帮助。
所以我在这里,寻找一个名字,甚至是一些图书馆,看看我还能用这个东西做什么。
user2407038's comment确实引起了人们的注意,因为您最终希望表达的概念归结为对某些对函子同构的函子-毕竟,对是附加了其他东西的值。从这个有利的角度出发,需要注意一些可能有趣的额外事情。
[为了方便起见,我假设您的Tag
是Functor
(您提到的相关条件由参数确定),并通过用同构forall b. b -> f b
替换f ()
来简化类型。然后,我们可能会有:
separate :: Tag f => f a -> (f (), a)
遵守法律:
slot . separate = id
其中左逆是:
slot :: Functor f => (f (), a) -> f a
slot (sh, a) = fmap (const a) sh
[通过添加slot
应该是实词的要求(就您的用例而言,这是完全明智的),将slot
升级为完全逆,从而使我们在f a
和[C0之间具有同构]。
这些功能在生态系统中至少存在一处:(f (), a)
:
the adjunctions package
Hask / Hask左伴随是同构的对函子,与Hask / Hask右伴随是splitL :: Adjunction f u => f a -> (a, f ())
unsplitL :: Functor f => a -> f () -> f a
的同义,即同构对函子。从某种意义上说,Representable
是合理的,尽管adjunctions并没有为我们提供Representable
或dfeuer's suspicion的左伴同伴。这是它们的草图,其中包含虚构的名称:
Distributive
Representable
连接的几点说明:
class Traversable t => Lone t where
codistribute :: Functor f => t (f a) -> f (t a)
surround :: Functor f => (a -> f b) -> t a -> f (t b)
class Lone f => Detachable f where
type Corep f
cotabulate :: (Corep f, a) -> f a
coindex :: f a -> (Corep f, a)
和Traversable
分别只是codistribute
和surround
,除了sequenceA
约束放宽到traverse
(如果始终只有一个值,则不需要Applicative
) 。
通过相同的记号,Functor
和Applicative
(或cotabulate
和coindex
或slot
和separate
)可以看作是unsplitL
的体现(我们需要内容没有列表/向量,因为同样总是总有一个值)。
对的各种实例可以通过splitL
进行翻译。对于the shape-and-contents decomposition of traversable functors,甚至不需要,因为Detachable
就足够了(另请参见adjunctions的Comonad
)。
[Lone
是van Laarhoven镜头(用[[lens来说是extractL
and duplicateL
),就像extractL
是van Laarhoven遍历(duplicateL
)。
surround
是您的Lens (t a) (t b) a b
的概括。