我是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
并在我愿意的时候添加到此列表中。显然我不能这样做,但有一些非可怕的方法可以达到同样的效果吗?
答案 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
。
顺便说一句,这是使用lens
库Isomorphism
中使用的类似技术完成的。同构基本上是一对彼此相反的函数。例如(+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)
事实上,你在函数值上没有可靠的身份 - 因此我认为没有可行的方法去做你想做的事。