简单的Haskell函数采用无点样式

时间:2011-12-11 16:34:03

标签: haskell pointfree

我试图了解如何在Haskell中将函数转换为无点表示法。我看到this example,但它比我想要的更复杂。我觉得我理解它背后的逻辑,但当我试图在代码中执行一些简单的例子时,我得到了编译错误。我想尝试以无点样式编写这个函数:

f x = 5 + 8/x我将其重新排列为f x = (+) 5 $ (/) 8 x

所以,我认为它可能是这样的:

f = (+) 5 $ (/) 8

但是当我在ghci中运行时,我收到了这条消息:

No instance for (Num (a0 -> a0))
  arising from the literal `5' at Test.hs:3:9
Possible fix: add an instance declaration for (Num (a0 -> a0))
In the first argument of `(+)', namely `5'
In the first argument of `($)', namely `(+) 5'
In the expression: (+) 5 $ (/) 8
Failed, modules loaded: none.

我不明白“没有实例......”的信息。如何以无点样式编写此函数需要做什么?

4 个答案:

答案 0 :(得分:17)

$的优先级非常低。因此,f x = (+) 5 $ (/) 8 x实际上意味着f x = (+) 5 $ ((/) 8 x)。相反,将其重写为

f x = (+) 5 ( (/) 8 x)
f x = ((+) 5) ( ((/) 8) x)
f x = ((+) 5) .  ( ((/) 8) ) x
f = ((+) 5) . ( (/) 8 )
f = (5+) . (8/)

最后一个表达式是有意义的:f是两个操作的组合,首先将8除以其中的一个,然后将5添加到结果中。请记住,g.h表示“应用h,然后应用g的结果”。

答案 1 :(得分:11)

“pointfree”程序可以与cabal install pointfree一起安装,并向您展示如何以无点样式编写表达式。例如:

$ pointfree "f x = 5 + 8/x"
f = (5 +) . (8 /)

此转换的说明:

  1. 您可以将“sections”用于中缀/操作员功能。 (a +) == \b -> a + b(+ a) == \b -> b + a
  2. .函数获取第二个参数的结果,该参数是单参数函数,并将其应用于第一个参数。

答案 2 :(得分:11)

从lambda演算(Haskell是其变体)转换为SKI术语(完全无点函数,仅使用const K ),id(< strong>我)和<*> S ))可以使用以下简单规则完成:

  1. \x -> x转换为id;
  2. \x -> y中没有x的{​​li> y转换为const y;
  3. \x -> f g转换为f' <*> g'其中
    • f'\x -> f
    • 的翻译
    • g'\x -> g
    • 的翻译
  4. 现在你可能想知道.在哪里进来了。最后一个翻译有一个特例:如果f没有任何x的自由出现,那么{{ 1}}转换为\x -> f g,等于const f <*> (\x -> g)

    使用这些规则我们可以转换您的功能:

    f . (\x -> g)

    Eta-reduction不是完成翻译所必需的,但如果没有它,我们就会变得更加混乱。例如,最后一步将产生f = \x -> ((+) 5) (((/) 8) x) = -- by the special-case (.) rule ((+) 5) . (\x -> (((/) 8) x)) = -- by eta-reduction ((\x -> f x) = f) ((+) 5) . ((/) 8)

答案 3 :(得分:4)

你真的很亲密。请允许我再添加一个$来说明:

f x = (+) 5 $ (/) 8 $ x

应该很清楚,表达式(+) 5是一个函数,它接受一个数字输入并产生一个数字输出。表达式(/) 8也是如此。因此,您输入任意数字x,然后首先应用(/) 8“函数”,然后应用(+) 5“函数”。

每当你有一串由$分隔的函数时,你可以用.意义替换除最右边的所有函数,如果你有a $ b $ c $ d,这相当于a . b . c $ d }。

f x = (+) 5 . (/) 8 $ x

此时,让我们实际上删除 $并改为括号。

f x = ((+) 5 . (/) 8) x

现在应该清楚的是,您可以从双方删除尾随的x

f = (+) 5 . (/) 8

是主要想法。如果您有f x = expr x,则可以将其“减少”到f = expr。为了生成无点代码,您只需要识别较大的函数如何由较小的函数组成。对于无点代码,有时需要部分应用(在这种情况下,部分应用(+) 5(/) 8)。当您不想考虑它时,“免费”程序非常有用; #haskell irc频道上的Lambdabot将此程序用作插件,因此您甚至不必自己安装它;请问:

<DanBurton> @pl let f x = 5 + 8 / x in f
<lambdabot> (5 +) . (8 /)