在Haskell中使用Rank2多态函数

2vuwiymt  于 2023-10-19  发布在  其他
关注(0)|答案(1)|浏览(131)

我想写一个这样的函数

inTransform tr f = tr . f . tr

但我很难把它打对。
我尝试使用Rank 2存在类型,如

inTransform :: (forall a. a -> a) -> (b -> c) -> (b -> c)

但当我尝试使用它时,例如,

inTransform reverse id [1, 6]

我有奇怪的错误,

Couldn't match type ‘a1’ with ‘[a0]’
      Expected: a1 -> a1
        Actual: [a0] -> [a0]
      ‘a1’ is a rigid type variable bound by
        a type expected by the context:
          forall a1. a1 -> a1

因此,存在类型可能不是正确的解决方案?
--编辑--
好的,建议的类型

inTransform :: (a -> b) -> (b -> a) -> a -> b

但实际上不适用于更复杂的用例,例如:

inTransform reverse (:[]) [1, 6]

问题是tr函数的类型是a -> a,但在之后应用时可能不是相同的a。这就是为什么我在考虑应用存在Rank 2类型

e7arh2l6

e7arh2l61#

{-# LANGUAGE UnicodeSyntax #-} -- ← should always be enabled

虽然rank-2类型的工作是允许在不同类型的上下文中使用转换,但∀ a. a -> a类型太强了,无法在任何非平凡的转换中使用它。
对于您的特定示例,将其限制为列表将有效:

inTransform :: (∀ a. [a] -> [a]) -> ([b] -> [c]) -> ([b] -> [c])

> inTransform reverse (map realToFrac) [1,2,3]
[1.0,2.0,3.0]

但是你可能会发现这太具体了,不能保证抽象。
一个折衷的方法是选择一个任意类型的构造函数,函数在该构造函数上被普遍量化,但在该构造函数的类型参数中保持秩为2:

inTransform :: ∀ f b c. (∀ a. f a -> f a) -> (f b -> f c) -> (f b -> f c)

这同样适用于反转列表,但也可以用于自逆多态函数的其他用例,只要多态参数和结果共享外部形状。

相关问题