F# 中的累加器生成器
为了了解更多 F#,我尝试实现 Paul Graham 此处。到目前为止,我最好的解决方案是完全动态类型:
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
的类型注释,代码将无法编译,因为编译器推断类型int ->; obj
for x - 尽管acc
被注释为返回obj->obj
。这是为什么?我可以避免吗? - 有什么想法可以改进这个动态类型版本吗?
- 是否可以使用适当的静态类型来实现它?也许有成员限制? (在 Haskell 中是可能的,但在 OCaml 中不行,AFAIK)
In my quest to learn more F#, I tried to implement an "accumulator generator" as described by Paul Graham here. My best solution so far is completely dynamically typed:
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"
I have three questions:
- If I remove the type annotation for
x
, the code fails to compile because the compiler infers typeint -> obj
for x - althoughacc
is annotated to return anobj->obj
. Why is that and can I avoid it? - Any ideas to improve this dynamically typed version?
- Is it possible to implement this with proper static types? Maybe with member constraints? (It is possible in Haskell, but not in OCaml, AFAIK)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这个问题需要存在一个未指定的数字塔。 Lisp 恰好有一个,而且它恰好足以满足 Paul Graham 的例子,因为这个问题是专门设计来让 Lisp 看起来人为的好。
您可以在 F# 中使用联合类型(例如
type number = Int of int | Float of float
)或通过装箱所有内容来实现数字塔。以下解决方案使用后一种方法:然而,数字塔在现实世界中几乎没有用处。除非你非常小心,否则试图从这些无聊的挑战中学习只会弊大于利。您应该问自己为什么我们不想要数字塔,不想要装箱并且不想要运行时类型提升?
为什么我们不直接写:
我们在每一步都知道
x
的类型。每个号码均未装箱存储。我们知道这段代码永远不会产生运行时类型错误......This problem requires the existence of an unspecified numeric tower. Lisp happens to have one and it happens to be adequate for Paul Graham's examples because this problem was specifically designed to make Lisp look artificially good.
You can implement a numeric tower in F# either using a union type (like
type number = Int of int | Float of float
) or by boxing everything. The following solution uses the latter approach:However, numeric towers are virtually useless in the real world. Unless you are very careful, trying to learn from these kinds of borked challenges will do you more harm than good. You should leave asking yourself why we do not want a numeric tower, do not want to box and do not want run-time type promotion?
Why didn't we just write:
We know the type of
x
at every step. Every number is stored unboxed. We know that this code will never produce a run-time type error...我同意 Jon 的观点,即这是一个相当人为的示例,它不是学习 F# 的良好起点。但是,您可以使用静态成员约束来相当接近,而无需动态转换和反射。如果将其标记为
inline
并使用float
添加转换两个参数:您将得到一个具有以下类型的函数:
该函数采用任意两个可以是的参数显式转换为浮点数。与 LISP 版本相比(我猜)的唯一限制是它始终返回 float(作为最通用的可用数字类型)。你可以这样写:
I agree with Jon that this is quite artificial example and it is not a good starting point for learning F#. However, you can use static member constraints to get reasonably close without dynamic casts and reflection. If you mark it as
inline
and add convert both of the parameters usingfloat
:You'll get a function with the following type:
The function takes any two arguments that can be explicitly converted to float. The only limitation compared to the LISP version (I guess) is that it always returns float (as the most universal numeric type available). You can write something like:
使用正确的静态类型绝对不可能实现这一点。你说你可以用 Haskell,但我不相信你。
It's definitely not possible to implement this with proper static types. You say you can in Haskell, but I don't believe you.
尝试使用静态类型来做到这一点的问题在于添加两个可能不同类型的不同数量,同时保留左侧的类型。正如 Jon Harrop 所说,这对于联合类型是可能的。一旦定义了联合类型和相应的加法运算(如前面提到的那样工作),实际的累加器就非常简单。我的实现:
The problem with trying to do this with static typing is in adding two different numbers of possibly different types while preserving the type of the left-hand side. As Jon Harrop says this is possible with a union type. Once you've defined the union type and a corresponding addition operation which works as mentioned, the actual accumulator is very simple. My implementation: