我正在尝试实现一个函数f
,以便在调用时如下所示:
(((f 1) 2) 3) ()
它返回1,2和3之和,即6,只有当它用单位参数()
调用时才会发生计算并返回总和。
例如,在Javascript(ES6)中,它可以这样实现:
let f = (v, acc = 0) => {
if (typeof v === 'undefined') return acc;
return next => f(next, acc + v);
};
f(1)(2)(3)(); // 6
然而,在具有强类型的Ocaml中,它不是清晰的,或者至少不是简洁的。
编辑:这是我尝试使用变体类型:
type any =
| Nothing
| Number of int
type result =
| Int of int
| Fn of (any -> result)
let rec sumfun ?(acc=0) v =
match v with
| Nothing -> Int(acc)
| Number n -> Fn(fun next -> sumfun ~acc:(acc+n) next)
let _ =
let a = sumfun (Number 2) in
match a with
| Int n -> print_int n
| Fn f ->
let b = f (Number 3) in
match b with
| Int n -> print_int n
| Fn f ->
let c = f Nothing in
match c with
| Int n -> print_int n
| Fn f -> ()
因为对sumfun
的调用超级毛茸茸。是否有比这更好,更冗长或更惯用的方式?
答案 0 :(得分:4)
正如评论中所指出的,一般的建议是不要做这样的事情。您的特定示例自然会用列表表示:
let sum = List.fold_left (+) 0
let n = sum [1; 2; 3]
我认为使用您展示的复杂结构没有任何优势,无论其类型如何。
但无论如何要回答你原来的问题,你可以做的最好的事情就是提供应用程序的自定义操作符,例如
let ($) f x = match f (Number x) with Fn f' -> f' | _ -> assert false
let ($$) f () = match f Nothing with Int n -> n | _ -> assert false
let n = sumfun $ 1 $ 2 $ 3 $$ ()
(我尝试使用GADT来避免那里的assert false
,但显然它们与可选参数的交互很糟糕。)
但重复一点:不要。
答案 1 :(得分:2)
我完全赞同安德烈亚斯,但我有另一个解决方案与GADT:
type 'a args = | M : int -> ('b args -> 'b) args
| O : int -> (unit -> int) args
let rec f : type a. a args -> a = function
| M n -> (function | M m -> f (M (m + n))
| O m -> fun () -> n + m)
| O n -> fun () -> n
你需要区分部分应用程序和最后一个应用程序,所以这里有两种参数:meta M
和object O
(我不知道为什么选择这些定义)。
所以,现在我们有:
# (((f (M 1)) (M 2)) (O 3)) ();;
- : int = 6
或使用$
表示法:
let ($) f i = f i
# f $ (M 1) $ (M 2) $ (O 3) $ ();;
- : int = 6
这里重要的是“错误”表达式(如f $ (M 1) $ (M 2) $ ()
)在编译时被类型检查拒绝,而在运行时没有异常。