haskell 如何写入“twice”以便它可以接受“swap”而不限制其类型

lfapxunr  于 2022-11-14  发布在  其他
关注(0)|答案(2)|浏览(151)

如果我有这些定义

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

g52tjvyc

g52tjvyc1#

这很难做到,因为你需要表达结果类型和参数类型是多态的,但在一个特定的函数关系中。你需要以显式的方式提供这种关系。表达类型级函数的最常见的方法是使用类型族,但这类函数不能被命名为一级实体,也不能像swap那样编码类型关系的双向性质。
你可以用一个唯一的类型标记来表示函数,然后用一个MPTC将参数和结果类型链接在一起。

{-# LANGUAGE FunctionalDependencies, AllowAmbiguousTypes #-}

class PolyFun f a b | f a -> b, f b -> a where
  once :: a -> b

data Swap

instance PolyFun Swap (a,b) (b,a) where
  once = swap

现在,
第一个
这是相当通用的,
第一个
但是它也有它的局限性,特别是我在上面写的方法,函数的参数和结果必须可以相互推断。但是对于许多函数来说,只有参数类型可以从结果类型推断出来,反之则不行,或者有时只能从参数推断出结果类型。你必须使用不同的类来覆盖类型信息可以流动的所有可能的方式,我不认为有一个单一的方法,可以自动完成这一切。

编辑事实证明,* 实际上 * 有一种方法可以自动完成,但是,呃... * 不要在家里尝试,孩子们 *...

一个

apeeds0o

apeeds0o2#

是的,但不是很有用。在不改变twice的实现的情况下,你可以改变它的类型。如果你改变它的类型,使其需要一个特别像swap的函数(即,它将类型参数的顺序翻转到某个类型构造函数,而不关心这些类型参数的细节),那么你可以将该函数与它本身组合在一起。

{-# LANGUAGE RankNTypes #-}

twice :: (forall a b. f a b -> f b a) -> f a b -> f a b
twice f = f . f

> :t twice swap
twice swap :: (a, b) -> (a, b)

你不能直接在flip上使用这个函数,因为currying会以错误的顺序关联类型参数。但是如果你明确表示这个函数是双参数的,你可以使用newtype来重新排序:

newtype BiFunction r a b = Bi {unBi :: a -> b -> r}
flipBi = Bi . flip . unBi

> :t twice flipBi
twice flipBi :: BiFunction r a b -> BiFunction r a b

问题是,这使得twice对于任何其他任务都毫无用处。例如,您不能再编写twice succ来获得一个加2的函数,因为succ没有对类型参数进行重新排序的属性。twice f几乎必须是恒等函数,因为它根本不能检查或修改ab类型的值,但它可以更改f中的上下文,因此您可以定义someFunctionPairEmpty,使得twice someFunction (Pair 1 2) = Empty。如果你觉得这是个有趣的练习,那就试试看。

相关问题