为了学习更多F#,我尝试实现Paul Graham here所描述的“累加器生成器”。到目前为止,我最好的解决方案是完全动态键入:

open System

let acc (init:obj) : obj->obj=
  let state = ref init
  fun (x:obj) ->
    if (!state).GetType() = typeof<Int32>
       && x.GetType() = typeof<Int32> then
      state := (Convert.ToInt32(!state) + Convert.ToInt32(x)) :> obj
    else
      state := (Convert.ToDouble(!state) + Convert.ToDouble(x)) :> obj
    !state

do
  let x : obj -> obj = acc 1  // the type annotation is necessary here
  (x 5) |> ignore
  printfn "%A" (x 2)   // prints "8"
  printfn "%A" (x 2.3) // prints "10.3"

我有三个问题:
  • 如果删除了x的类型注释,则代码将无法编译,因为编译器会推断出x的int -> obj类型,尽管acc带有注释以返回obj->obj。为什么会这样,我可以避免吗?
  • 有什么想法可以改进此动态类型的版本吗?
  • 是否可以使用适当的静态类型来实现?也许有会员限制? (在Haskell中是可能的,但在OCaml和AFAIK中则没有)
  • 最佳答案

    为了学习更多F#,我尝试实现Paul Graham在此描述的“累加器生成器”。

    此问题需要存在未指定的数字塔。 Lisp恰好有一个,并且恰好适合Paul Graham的例子,因为这个问题是专门为使Lisp看起来人为地设计的。

    您可以使用联合类型(例如type number = Int of int | Float of float)或通过将所有内容装箱在F#中实现数字塔。以下解决方案使用后一种方法:

    let add (x: obj) (y: obj) =
      match x, y with
      | (:? int as m), (:? int as n) -> box(m+n)
      | (:? int as n), (:? float as x)
      | (:? float as x), (:? int as n) -> box(x + float n)
      | (:? float as x), (:? float as y) -> box(x + y)
      | _ -> failwith "Run-time type error"
    
    let acc x =
      let x = ref x
      fun (y: obj) ->
        x := add !x y
        !x
    
    let x : obj -> _ = acc(box 1)
    do x(box 5)
    do acc(box 3)
    do printfn "%A" (x(box 2.3))
    

    但是,数字塔在现实世界中几乎没有用。除非您非常谨慎,否则尝试从这些沉闷的挑战中学习将使您弊大于利。您应该问自己,为什么我们不想要数字塔,不想装箱并且不想运行时类型提升?

    我们为什么不只写:
    let x = 1
    let x = x + 5
    ignore(3)
    let x = float x + 2.3
    

    我们知道每一步x的类型。每个数字都未装箱存储。我们知道这段代码永远不会产生运行时类型错误...

    关于f# - F#中的累加器生成器,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/3644107/

    10-10 17:37