任意类型的函数调用

时间:2018-02-24 05:35:14

标签: javascript ecmascript-6 functional-programming ocaml

我正在尝试实现一个函数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的调用超级毛茸茸。是否有比这更好,更冗长或更惯用的方式?

2 个答案:

答案 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) $ ())在编译时被类型检查拒绝,而在运行时没有异常。