T - 标记
好的,我想重命名您的类型以帮助理解它们。 T 将数字分配给值,并且由于我不确定容器的结构是什么(并且希望保持灵活),因此我将在一对 (x,n) 中弹出值和数字。让我们称之为标记,所以重命名TTagger,所以假设我知道你的集合类型是CollXs 的集合将具有类型Coll X,所以我需要
type CollTaggerXInt = Coll X -> Coll (X,Int)
它为你想要的函数创建了一个类型的同义词。
但是如果Int 太小而您想使用Integer、Double 或其他数字类型怎么办?
data Num n => CollTaggerX n = CollTaggerX (Coll X -> Coll (X,n))
这意味着您可以使用任何固定类型的数字数据标记X 值。 (Num n => 是一个数据类型约束,它断言 n 必须是数字类型。)右侧的 CollTaggerX 通过将标记函数包装在轻量级构造函数中来确保类型安全。我们需要使用data 而不是type,因为我已经通过n 进行了参数化。
我倾向于将固定类型X 和集合类型Coll 替换为类型参数(如某些语言中的泛型,例如Java),以提高代码重用性:
data (Functor coll,Num n) => Tagger coll x n = Tgr (coll x -> coll (x,n))
所以现在我们坚持 collection 类型是 Functor,因此我们可以使用 fmap 将函数逐点应用于集合(在您的情况下至关重要,任何集合类型都将是 Functor 的实例) .
我对@987654343@ 的T 定义最满意,但如果coll、x 和n 只有一种可能性,您可以使用CollTaggerXInt。
U - 制作标签
您的U 类型用于将元素标记器转换为集合标记器。我想叫它Lifting,而不是U。如果您使用CollTaggerXInt,您可以再次使用类型同义词:
type LiftXIntToTagger = (X -> Int) -> Coll X -> Coll (X,Int)
或者如果您使用更灵活的Tagger 定义,您可以编写
data (Functor coll,Num n) => Lifter coll x n = Lifter ((x -> n) -> coll x -> coll (x,n))
但是为此创建一个类型感觉很疯狂,因为如果你有一个逐点函数,你可以使用 fmap 来提升它,它仍然适用于你的 coll 类型:
fmap :: Functor f => (a -> b) -> f a -> f b
所以我们可以将coll 用作f,x 用作a 和(x,n) 用作b,并定义
liftT :: (Functor coll,Num n) => (x -> n) -> coll x -> coll (x,n)
liftT f = fmap tag where
tag x = (x,f x)
如果你想定义你的类型,好的,但我认为liftT 可能是你的类型U 中唯一合理的函数。
排名 - U+上下文
现在我认为您的排名示例很有用,所以让我们调查一下。 rankfunction 需要检查集合的 所有 元素,所以让我们将整个集合作为它的第一个参数,即 rankfunction :: coll x -> x -> n(在上下文 (Functor coll,Num n) 内)。
liftInContext :: (Functor coll,Num n) => (coll x -> x -> n) -> coll x -> coll (x,n)
liftInContext rankfunction mycoll = liftT (rankfunction mycoll) mycoll
这里的函数(rankfunction mycoll) 将它的第一个参数——整个集合——传递给rank函数,然后使用liftT 将它应用于每个元素。这称为部分评估,对于这类事情非常方便。