是否有可能根据具体情况定义更高阶的“相反”函数?

时间:2014-06-16 10:16:40

标签: haskell

我是Haskell的新手。我试图在Haskell中创建一个迷你语言,并希望有一个更高阶的函数叫做opp(“对立”的缩写),它将许多熟悉的函数转换成明显的对立面。例如,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 = last

opp last = head

并在我愿意的时候添加到此列表中。显然我不能这样做,但有一些非可怕的方法可以达到同样的效果吗?

3 个答案:

答案 0 :(得分:11)

是的,你可以,但是你需要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类。这只描述了一个f a b,它可以用(a -> b)的两个函数构建(正常和相反的函数)。

接下来,定义数据类型FunctionAndOpposite。这只是存储这两个功能。既是标准函数,又是类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


顺便说一句,这是使用lensIsomorphism中使用的类似技术完成的。同构基本上是一对彼此相反的函数。例如(+2)(-2)reverse及其本身。这通常可能是一个更有用的概念,因为它允许您通过转换对数据类型运行操作。有关详细信息,请参阅Control.Lens.Iso,但请注意,要了解这一点,您需要了解镜头概念,这是一项相当大的工作。

答案 1 :(得分:2)

如果我们将您的问题重新表述为此形式,问题可能会变得更加明确:

opp x | x == succ = pred
      | x == pred = succ

这将为您提供(a -> b)没有Eq实例的错误,由于函数的相等性未定义,因此无法实现该错误。

对此的解决方案是定义一个可以模式匹配的单独数据类型。

答案 2 :(得分:0)

有可能使用某种基于堆上的闭包地址的哈希映射来识别函数。然后你可以创建这样一个逆表。不幸的是,你不会真正得到你想要的东西,因为函数只是值,因此只要编译器(或运行时)决定这样做就会创建它们。

E.g。即使你说那个

opp head = last

您可能(取决于编译器实现)无法实现

opp (λx.head x) 

事实上,你在函数值上没有可靠的身份 - 因此我认为没有可行的方法去做你想做的事。