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

use*_*553 7 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

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

Dav*_*ani 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
Run Code Online (Sandbox Code Playgroud)

用法示例:

> head' [1,2,3]
1
> opp head' [1,2,3]
3
Run Code Online (Sandbox Code Playgroud)

这个怎么运作

首先,有Opposite班级.这只是描述了一个f a b可以用(a -> b)(正常函数和相反函数)两个函数构造的函数.

接下来,FunctionAndOpposite定义数据类型.这只是存储这两个功能.现在这两个标准函数,都是这个类的实例Opposite.函数实例只丢弃相反的函数.

现在opp可以定义该功能.这只需要第二个功能FunctionAndOpposite.

最后,我们得到了将它们汇集在一起​​的线:

type a :<-> b = forall f. Opposite f => f a b
Run Code Online (Sandbox Code Playgroud)

它定义的是一个类型同义词,它接受两种输入类型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,但请注意,要了解这一点,您需要了解镜头概念,这是一项非常重要的工作.


cho*_*ger 0

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

\n\n

例如,即使你这么说

\n\n
opp head = last\n
Run Code Online (Sandbox Code Playgroud)\n\n

您可能(取决于编译器实现)一无所获

\n\n
opp (\xce\xbbx.head x) \n
Run Code Online (Sandbox Code Playgroud)\n\n

事实上,您在函数值上没有可靠的身份 - 因此我认为没有可用的方法来完成您想做的事情。

\n