F# 反咖喱?

F# 反咖喱?,f#,functional-programming,currying,higher-order-functions,F#,Functional Programming,Currying,Higher Order Functions,我想以某种方式组合函数。请考虑这2个函数在伪码(非f<)中的作用。 我想让F#做的是弄清楚 let F1 x y = x + y //val F1 : int -> int -> int 代码let F2=F1*10将给我与F1相同的签名:val F2:int->int->int,调用F2 23将得到50:(2+3)*10。那将是相当聪明的 所发生的事情与之大不相同。第一行如预期的那样: let F1 x y = x + y //val F1 : int -> int -&g

我想以某种方式组合函数。请考虑这2个函数在伪码(非f<)

中的作用。 我想让F#做的是弄清楚

let F1 x y = x + y
//val F1 : int -> int -> int
代码
let F2=F1*10
将给我与F1相同的签名:
val F2:int->int->int
,调用
F2 23
将得到50:(2+3)*10。那将是相当聪明的

所发生的事情与之大不相同。第一行如预期的那样:

let F1 x y = x + y
//val F1 : int -> int -> int
但是当我添加第二行时,让F2=F1*10它抛出F。它抱怨
类型int与类型'a->'b->'c
不匹配,F1现在
需要成员(+)

我当然可以这样说:

let F1(x, y) = x + y
let F2(x, y) = F1(x, y) * 10
但现在我还不如用C#,我们已经不那么遥远了。元组参数打破了F#的许多优雅。而且,我的实函数F1和F2的参数比2多得多,所以这让我很生气,这正是我想通过使用F#来回避的。这样说会更自然:

let F1 x y = x + y
let F2 = F1 * 10
我有什么办法(几乎)做到这一点吗

额外积分:这些错误消息到底是怎么回事?为什么第二行
让F2=F1*10
更改第一行的键入

提前感谢您的想法

格特扬

更新 两种方法(几乎)达到了所描述的效果

一个使用元组的。第二行看起来有点古怪,第一行很好用。小缺点是我现在不能使用咖喱,否则我将不得不添加更古怪的代码

let F1 (a, b) = a + b
let F2 = F1 >> (*) 10

F2(2, 3) // returns 50
另一种方法是使用记录。这更直截了当,乍一看更容易理解,但需要更多的代码和仪式。确实去掉了F#的一些优雅,看起来更像C#

那将是相当聪明的

如此聪明,它将击败地狱的类型系统。您需要的是APL中的阵列编程

我有什么办法(几乎)做到这一点吗

我不会说F#,但在哈斯克尔,你会不停地用
F1
,然后用
*10
作曲,然后用咖喱:

f2 = curry ((*10) . uncurry f1)
在ML方言中,如F#变成了:

let curry f x y = f (x,y)
let uncurry f (x,y) = f x y

let mult x y = x * y

let F1 x y = x + y
let F2 = curry (uncurry F1 >> mult 10)

(我不确定
curry
uncurry
是否在F#标准库中,所以我定义了它们。也可能有一种更漂亮的方法,可以在不定义
mult
的情况下部分应用
*

一般来说,这没有模式。按照larsmans的建议,使用组合词(如
curry
uncurry
)是一种选择,但我认为其结果比显式版本可读性差、时间长

如果经常使用此特定模式,则可以定义一个运算符,用于将函数(具有两个参数)乘以标量:

let ( ** ) f x = fun a b -> (f a b) * x

let F1 x y = x + y
let F2 = F1 ** 10
不幸的是,您无法将标准数字运算符(
*
等)的实现添加到现有类型(例如
'a->'b->int
)。然而,这是一个非常频繁的请求(对于其他事情也很有用)。或者,您可以将函数包装到一些对象中,这些对象提供重载的数字运算符(并包含一些运行函数的
Invoke
方法)

我认为一个合适的名称应该是lifting-您将
*
操作符(处理整数)提升到一个处理返回整数的函数的版本。这与使用
*
处理可空类型时在C#编译器中执行的提升类似

要解释错误消息-它抱怨表达式
F1*10

错误FS0001:类型“int”与类型“a->”b->“c”不匹配

我认为这意味着编译器正试图为
*
操作符找到一个实例化。从右边看,它认为这应该是
int
,所以它认为左边也应该是
int
,但它实际上是两个参数的函数-类似于
'a->'b->c'
,顺便说一句,使用无点(或者在这种情况下是无意义的)方法一可以用以下方式定义这些功能:

let F1 = (+)
let F2 = (<<)((*)10) << F1
让F1=(+)

让F2=(看看Haskell中的合成。这基本上是同一个问题,但有一些有趣的解决方案可能会在F#中实现(我不是母语人士).嗨,哈马尔,谢谢,哈斯克尔让我觉得自己有诵读困难症..我想这就是他们说的“浓缩汤”的意思。我稍后会试试看,也许哈斯克尔所有人看起来都像是一个很好的“心灵健身房”。谢谢!如果
F1
取了一个元组,那么
F2
可以拼写为
let F2=F1>(*)10
@ildjarn谢谢你的例子,我一开始没有采用元组方法。我也用这种方法更新了OP。“聪明”哈哈,我想是的。当我描述我真正想做的事情时,APL或haskell往往会突然出现。这么多漂亮的语言,这么少的时间。让我试着了解一下你和Tomas说的话,然后回到这里。我喜欢这种方法的抽象,其中函数是curried和uncarried,尽管它们对于2个参数也是特定的。在F#中也没有找到任何通用运算符,Haskell可以对任何函数这样做?这很好。但是,这种技术确实引入了相当多的“技术噪音”,其中有很多代码是at是关于拼写“如何”而不仅仅是“什么”。理想情况下,我希望得到的代码读起来就像你在白板上为非prog编写的代码一样,但我想你是对的,对于所有不同的情况,解析器/类型系统都很难计算。@gjvdkamp:在Haskell中,某种类型类魔法可能会起作用。我不知道如果F#支持类型类,我必须研究一下。我改用F#的主要原因是能够更“直观”地编写代码,而不需要所有花括号、返回语句和可变变量
let ( ** ) f x = fun a b -> (f a b) * x

let F1 x y = x + y
let F2 = F1 ** 10
let F1 = (+)
let F2 = (<<)((*)10) << F1