【问题标题】:Rewriting Erlang in F#在 F# 中重写 Erlang
【发布时间】:2016-03-23 09:03:11
【问题描述】:

我找到了 Don Syme 的 presentation,它表明 Erlang 的

fac(0) -> 1
fac(N) -> N * fac(N-1).

相当于F#的

let rec fac = function
| 0 -> 1
| n -> n * fac (n-1)

但看起来没有办法在不丢失类型安全的情况下将模式匹配用于不同的数量。例如。可以使用列表模式匹配,但类型必须是通用基本类型(例如object):

let concat = function
    | [x;y] -> x.ToString() + y.ToString()
    | [x] -> x.ToString()

鉴于模块中的 F# 函数不支持重载,看来使用静态类型将 Erlang 代码重写为 F# 的唯一方法是使用具有方法重载的静态类而不是模块。有没有更好的方法在 F# 中重写具有不同数量的 Erlang 函数?

一般来说,说 Erlang 的参数匹配更接近 .NET(包括 C#)的方法重载而不是 F# 的模式匹配是否正确?或者两者之间没有直接替代,例如Erlang 中可能有一个具有不同元数的函数 + 一个守卫:

max(x) -> x.
max(x,y) when x > y -> x.
max(x,y) -> y.
max(comparer, x, y) -> if comparer(x,y) > 0 -> x; true -> y end.

在最后一种情况下,参数是不同的类型。你会如何用 F# 重写它?

【问题讨论】:

    标签: f# erlang


    【解决方案1】:

    您可以通过稍微重新考虑问题来实现接近重载的目标。与其将函数视为可变性轴,不如将输入视为可变部分。如果你这样做,你会意识到你可以achieve the same with a discriminated union

    这是一个比链接文章中的更人为的例子:

    type MyArguments = One of int | Two of int * int
    
    let foo = function
        | One x -> string x
        | Two (x, y) -> sprintf "%i%i" x y
    

    用法:

    > foo (One 42);;
    val it : string = "42"
    > foo (Two (13, 37));;
    val it : string = "1337"
    

    显然,与其定义上述MyArguments 这样的“愚蠢”类型,您应该定义一个在您正在建模的领域中有意义的可区分联合。

    【讨论】:

    • 很好,它还允许使用守卫:Two (x, y) when x > y。但是,如果某些 args 只是可选的,我正在努力命名这些案例:例如Default, DefaultWithComparer, ...我需要为每个参数组合命名,感觉有点奇怪。
    • @V.B.您通常可以通过创建输入类型的默认值来处理默认值。您不必将其设为特殊情况。例如,如果您的函数 always 需要一个比较器,则将比较器设置为输入类型的强制字段,然后提供具有默认比较器的该输入类型的默认值,或替代,提供一个函数,它使用默认比较器创建输入值。
    • 实际上,这不是我在之前的评论中想到的弱点,而是一种优势:消息传递是 Erlang 的核心,可以将每个 DU 案例视为消息的名称。每条消息都有自己的有线格式等。消息的显式命名——即使是冗长的——可以在开发/原型阶段增加可读性。然后可以将其替换为 blittable 结构以避免 DU 对象分配和序列化。
    猜你喜欢
    • 2015-03-09
    • 2016-12-04
    • 2010-12-08
    • 2013-11-05
    • 2015-08-15
    • 1970-01-01
    • 2015-11-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多