我就安利一个小众语言F#吧:
F#是C#的同门师兄弟,所以我就直接拿C#来比较。亮点有:
1、let,let 的语义是符号绑定一个值,而不是声明一个不可变变量,虽然实际上往往是通过后者实现,但是因为这个语义,我们思维上可以简化编程的逻辑。如:
let (a, b) = (b, a)
这种东西不需要在py(念piyan)里找,F#就可以,而且编译器优化效果很好。如
let Subtract a b =
let (a, b) = (b, a)
a - b
未优化前,ILSpy还原出的C#代码为:
public static int Subtract(int a, int b)
{
Tuple expr_07 = new Tuple(b, a);
int b2 = expr_07.Item2;
return expr_07.Item1 - b2;
}
Release版为:
public static int Subtract(int a, int b)
{
return b - a;
}
完全符合let 为值绑定的语义预期。但是实测,C#中,前者代码是不可以优化成后者的。
2、类型推导。让代码简洁:
static Func Fix(Func, Func> f)
{
return x => f(Fix(f))(x);
}
static Func Fix(Func, Func> f)
{
return (x, y) => f(Fix(f))(x, y);
}
我们换成F#怎样呢?
let rec Y f x = f (Y f) x
破案,有时候就这么简单。
3、对象表达式。
你只需要一个具有某个接口的的对象,但是不想为此创造一个类吗?C#不可以,但是F#可以。譬如,F#里面的lock比较丑,又要括号,又要写fun()。
我们自己通过IDisposable接口来写一个ulock来替代它:
let ulock lockObj =
let mutable token = false
try
Monitor.Enter (lockObj, &token)
{ new IDisposable with
member __.Dispose() = Monitor.Exit lockObj }
with _ ->
if token then Monitor.Exit lockObj
reraise()
使用时候只需要:
use __ = ulock xx
。。。。
当然,某些时候你还是不得已继续使用lock。(使用using关键字调用ulock完全等价于lock,只是代码还长一点)
4、inline。这个用法被归类到F#泛型的静态解析里面。它是F#自己特有的泛型。举个例子:
如果一个泛型函数,参数类型需要有一个名为「LongID」的属性该怎么做呢?正常情况下,需要这些类都继承某个接口,在接口里声明LongID属性,然后再在目标类里都实现接口。但是F#就只需要:
let inline getLongID (node: ^t) = (^t:(member LongID: string) node)
不需要用到接口,因为F#是静态语言,所以调用getLongID时,编译器可以检测你的对象是否具有LongID属性,再而针对调用它的类生成重载代码。这个功能是目前我认为最强大的,既模仿了动态语言的灵活性,又使用了静态语言的严格检测,甚至有时候能抢反射的饭碗,正确使用就可以避免接口满天飞的情况,写DSL时这特性很管用。但是只能F#使用,不能给C#做泛型调用,出问题后果自负。
========================
更新。有了inline 我们还可以再优化一下第2小节里面的Y组合子:
let inline Y f =
let rec y x =
f y x
y
这样当我们写代码执行Y f 调用时候,编译器直接生成里面的y函数。