如果我有这些定义
twice f = f . f
swap (x,y) = (y,x)
两次的类型推断为(a -> a) -> a -> a
,交换推断为(a,b) -> (b,a)
。
如果我写swap . swap
,这个表达式的类型是(a, b) -> (a, b)
。
但是如果我要求twice swap
,它的类型是(a, a) -> (a, a)
。
我知道twice
限制了swap
的类型,但我想知道是否有一种方法可以编写twice
,使其接受swap
而不限制其类型,也就是说,您可以编写twice swap
并接受每个组件具有不同类型的对。
同样的情况也发生在flip :: (a -> b -> c) -> b -> a -> c
上,因为twice flip :: (a -> a -> b) -> a -> a -> b
。
2条答案
按热度按时间g52tjvyc1#
这很难做到,因为你需要表达结果类型和参数类型是多态的,但在一个特定的函数关系中。你需要以显式的方式提供这种关系。表达类型级函数的最常见的方法是使用类型族,但这类函数不能被命名为一级实体,也不能像
swap
那样编码类型关系的双向性质。你可以用一个唯一的类型标记来表示函数,然后用一个MPTC将参数和结果类型链接在一起。
现在,
第一个
这是相当通用的,
第一个
但是它也有它的局限性,特别是我在上面写的方法,函数的参数和结果必须可以相互推断。但是对于许多函数来说,只有参数类型可以从结果类型推断出来,反之则不行,或者有时只能从参数推断出结果类型。你必须使用不同的类来覆盖类型信息可以流动的所有可能的方式,我不认为有一个单一的方法,可以自动完成这一切。
编辑事实证明,* 实际上 * 有一种方法可以自动完成,但是,呃... * 不要在家里尝试,孩子们 *...
一个
apeeds0o2#
是的,但不是很有用。在不改变
twice
的实现的情况下,你可以改变它的类型。如果你改变它的类型,使其需要一个特别像swap
的函数(即,它将类型参数的顺序翻转到某个类型构造函数,而不关心这些类型参数的细节),那么你可以将该函数与它本身组合在一起。你不能直接在
flip
上使用这个函数,因为currying会以错误的顺序关联类型参数。但是如果你明确表示这个函数是双参数的,你可以使用newtype来重新排序:问题是,这使得
twice
对于任何其他任务都毫无用处。例如,您不能再编写twice succ
来获得一个加2的函数,因为succ
没有对类型参数进行重新排序的属性。twice f
几乎必须是恒等函数,因为它根本不能检查或修改a
或b
类型的值,但它可以更改f
中的上下文,因此您可以定义someFunction
、Pair
和Empty
,使得twice someFunction (Pair 1 2) = Empty
。如果你觉得这是个有趣的练习,那就试试看。