社区Wiki问题:

根据这个问题:What are the benefits of using Scala in .Net?我想到另一个问题。谁能在.Net平台上列出Nemerle和F#的相对优势(和劣势)?我只是顺便看着Nemerle。听起来好像和F#在同一个舞台上,所以我想知道除了明显的语法差异以及Microsoft支持F#的巨大优势之外还有什么区别。

最佳答案

我已经接触过这两种语言,并且对Nemerle的印象简要如下:(我想大多数观众都熟悉F#,而Nemerle不那么受欢迎,因此为了公平起见,我将对其进行介绍):


F#社区相当大,并且由于博客帖子,文章等的大量增加而不断增长。它也遍布全国。相反,Nemerle爱好者基本上是说俄语的,并且集中在RSDN.ru网站上。
Nemerle语法对于具有C类语言背景的开发人员而言,IMO更加友好。
Nemerle(以及F#)具有类型推断功能。 Nemerle中的类型推断机制绑定到方法主体(局部函数,变量等),与F#全局类型推断范围相反。但是,Nemerle编译器不会强制执行任何特定的编写代码习惯来辅助类型推断机制。


F#

open System.Text

let l = [1; 2; 3]
let r1 = l |> List.fold(fun (sb : StringBuilder) v -> sb.Append(v).AppendLine()) (StringBuilder()) // type annotation is required on the function argument
let r2 = (StringBuilder(), l) ||> List.fold(fun sb v -> sb.Append(v).AppendLine()) //here compiler can infer type of State parameter


奈梅勒

using System.Console;
using System.Collections.Generic;
using System.Text;

def l = [1,2,3];
def res = l.FoldLeft(StringBuilder(), (v, acc) => acc.Append(v).AppendLine());
WriteLine($"Result:\n$res");

def d = Dictionary(); // generic parameters are absent (even placeholders!!!)
d.Add(1, "!");
WriteLine(d.GetType()); // System.Collections.Generic.Dictionary`2[System.Int32,System.String]


您可能还会注意到Nemerle编译器的另一个功能–它可以从进一步的使用中推断类型。为了推断类型,F#使用基于Hindley-Milner算法的方法,并尝试推断大多数通用类型。相反,Nemerle从不推断多态类型,并且始终寻找最特定的类型。

F#

let addInt = (+) 5
let addString = (+) "!!!"

let run f x = f (f x) // ('T -> 'T) -> 'T -> 'T

run addInt 5
run addString "S"


Nemerle在相同条件下将推断运行类型为(int-> int)* int-> int。

有关Nemerle类型推断机制的更多详细信息,请参见Michal Moskal的MSc论文:Type Inference With Deferral


Nemerle具有丰富的元编程功能。大多数语言控制结构,例如循环,条件表达式,LINQ支持,即将到来的C#源代码解析功能等等,所有这些都是使用宏创建的。可以在here中找到一个宏应用程序示例。顺便说一句,上面示例中带有$语法的字符串格式化功能-也是内置宏。


编辑:添加了稍大的示例

using System.Console;
using System.Collections.Generic;
using System.Text;

variant Expr
{
  | Const { value : double }
  | Var { name : string }
  | Operation { id : string; left : Expr; right : Expr }

  public Eval(operations : Dictionary[string, double*double -> double], context : Dictionary[string, double]) : double
  {
    match(this)
    {
        | Const (value) => value
        | Var(name) => context[name]
        | Operation(id, left, right) =>
            def f = operations[id];
            f(left.Eval(operations, context), right.Eval(operations, context))
    }
  }
}

module Program
{
    public Main() : void
    {
        def expr =
            Expr.Operation(
                "*",
                Expr.Const(10),
                Expr.Operation(
                "+",
                Expr.Var("n"),
                Expr.Const(5)
                )
            );
        def operations = Dictionary.[string, double * double -> double]();
        operations["+"] = (x, y) => x + y;
        operations["*"] = _ * _;

        def vars = Dictionary();
        vars["n"] = 3.0;

        def result = expr.Eval(operations, vars);
        WriteLine($"Result is $result");
    }
}

10-07 16:05