我注意到我有很多函数可以为我的值添加某种标记。例如,请考虑以下两种数据类型:
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就不是很有帮助。
所以在这里,我正在寻找一个名字,甚至可能还有一些图书馆,看看我还能做些什么呢。
答案 0 :(得分:0)
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
升级为完全反,从而使我们在{ {1}}和f a
。
这些功能至少存在于生态系统中的一个位置:the adjunctions package:
(f (), a)
Hask / Hask左伴随是同构的,以配对函子,双重性是Hask / Hask右伴随是Representable
的同义,即同构对函子。从某种意义上来说,dfeuer's suspicion是合理的,尽管附加语并没有给我们与splitL :: Adjunction f u => f a -> (a, f ())
unsplitL :: Functor f => a -> f () -> f a
或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
)可以被视为the shape-and-contents decomposition of traversable functors的一种表现形式(其中,我们不需要内容的列表/向量,因为同样,总是存在一个唯一的值)。
可以想象成对的各种实例可以通过splitL
进行翻译。对于Detachable
,甚至不需要,因为Comonad
就足够了(另请参见附加语中的extractL
and duplicateL
)。
Lone
是van Laarhoven镜头(用{em> lens 来说是surround
),就像Lens (t a) (t b) a b
是van Laarhoven遍历({ {1}}。
最后但并非最不重要的一点,traverse
是您的Traversal (t a) (t b) a b
的概括。