我是 Haskell 新手。我正在尝试在 Haskell 中创建一种迷你语言,并且希望如果可能的话有一个名为 opp
(“opposite”的缩写)的高阶函数,它将许多熟悉的函数转换为它们明显的函数对立面。例如,opp succ
将是函数 pred
,opp head
将是 last
,依此类推。对于将函数转换为相反函数的含义,我没有一些一般性定义:我只想挑选几个关键示例并声明它们的相反函数。所以我想要一个几乎从未定义的高度多态函数。
困难似乎在于我想通过名称而不是其本质(可以这么说)来识别函数。这种困难的一个表现是,如果我写
opp succ = pred
然后 Haskell 将 succ
视为变量,因此给我一个常量函数,该函数始终采用值 pred
。我真正想说的是,“如果您看到字符串 opp succ
,那么可以将其视为 pred
的另一个名称。”但经过一段时间的搜索后,我不知道如何做到这一点(如果可能的话)。
总而言之,我想定义一个函数
opp::(a -> b) -> (a -> b)
通过说类似的话
opp succ = pred
opp pred = succ
opp head = 最后
opp 最后 = 头
只要我愿意,就可以添加到这个列表中。显然我不能那样做,但是有没有一些不可怕的方法可以达到相同的效果?
最佳答案
是的,你可以,但是你需要 RankNTypes 才能有一个好的实现。
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE RankNTypes #-}
module Opposites where
class Opposite f where
makeOpposite :: (a -> b) -> (a -> b) -> f a b
data FunctionAndOpposite a b = FunctionAndOpposite (a -> b) (a -> b)
instance Opposite (->) where
makeOpposite = const
instance Opposite FunctionAndOpposite where
makeOpposite = FunctionAndOpposite
opp :: FunctionAndOpposite a b -> a -> b
opp (FunctionAndOpposite _ f) = f
type a :<-> b = forall f. Opposite f => f a b
succ' :: Enum a => a :<-> a
succ' = makeOpposite succ pred
pred' :: Enum a => a :<-> a
pred' = makeOpposite pred succ
head' :: [a] :<-> a
head' = makeOpposite head last
last' :: [a] :<-> a
last' = makeOpposite last head
使用示例:
> head' [1,2,3]
1
> opp head' [1,2,3]
3
工作原理
首先是Opposite
类(class)。这只是描述了一个f a b
可以由 (a -> b)
的两个函数构造而成(正常和相反的功能)。
接下来是数据类型 FunctionAndOpposite
被定义为。这仅存储两个函数。现在标准函数和 this 都是类的实例 Opposite
。函数实例只是丢弃相反的函数。
现在是opp
可以定义函数。这只是从 FunctionAndOpposite
中取出第二个函数。 .
最后我们得到了将所有内容组合在一起的行:
type a :<-> b = forall f. Opposite f => f a b
它定义的是一个类型同义词,它接受两个输入类型 a 和 b,然后返回类型 f a b
,其中f
可以是任何Opposite
(取决于需要什么)。
( :<->
只是使用 TypeOperators
启用的类型的一个奇特名称)。
考虑代码 head' [1,2,3]
。 head'
类型为 forall f. Opposite f => f [a] a
。但是,由于它被用作函数应用程序(因为它之后有一个参数),f
必须是->
。因此,Opposite
的函数实现使用时,将第一个参数返回给 makeOpposite
,即head
(太棒了!)。
但是,可以说 opp head' [1,2,3]
叫做。 opp
类型为 FunctionAndOpposite a b -> a -> b
,所以f
必须是FunctionAndOpposite
。因此,FunctionAndOpposite
Opposite
的实例被调用,使用 makeOpposite
的两个参数创建数据类型。 opp
然后提取第二个函数,即 last
.
顺便说一句,这是使用 lens
中使用的类似技术来完成的。 Isomorphism
的库。同构基本上是一对互为逆的函数。例如(+2)
和(-2)
, reverse
和它本身。一般来说,这可能是一个更有用的概念,因为它允许您通过转换对数据类型运行操作。请参阅Control.Lens.Iso有关更多详细信息,但请注意,要理解这一点,您需要了解镜头概念,这是一项相当大的工作。
关于haskell - 是否可以根据具体情况定义高阶 "opposite"函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24241367/