Is it possible to implement auto-currying to the Lisp-family languages?

前端 未结 6 2008
夕颜
夕颜 2020-12-31 06:15

That is, when you call a function with >1 arity with only one argument, it should, instead of displaying an error, curry that argument and return the resulting function with

6条回答
  •  醉梦人生
    2020-12-31 06:49

    It's possible, but not easy if you want a useful result.

    • If you want a language that always does simple currying, then the implementation is easy. You just convert every application of more than one input to a nested application, and the same for functions of more than one argument. With Racket's language facilities, this is a very simple exercise. (In other lisps you can get a similar effect by some macro around the code where you want to use it.)

      (Incidentally, I have a language on top of Racket that does just this. It gets the full cuteness of auto-curried languages, but it's not intended to be practical.)

      However, it's not too useful since it only works for functions of one argument. You could make it useful with some hacking, for example, treat the rest of the lisp system around your language as a foreign language and provide forms to use it. Another alternative is to provide your language with arity information about the surrounding lisp's functions. Either of these require much more work.

    • Another option is to just check every application. In other words, you turn every

      (f x y z)
      

      into code that checks the arity of f and will create a closure if there are not enough arguments. This is not too hard in itself, but it will lead to a significant overhead price. You could try to use a similar trick of some information about arities of functions that you'd use in the macro level to know where such closures should be created -- but that's difficult in essentially the same way.

    But there is a much more serious problem, at the highlevel of what you want to do. The thing is that variable-arity functions just don't play well with automatic currying. For example, take an expression like:

    (+ 1 2 3)

    How would you decide if this should be called as is, or whether it should be translated to ((+ 1 2) 3)? It seems like there's an easy answer here, but what about this? (translate to your favorite lisp dialect)

    (define foo (lambda xs (lambda ys (list xs ys))))
    

    In this case you can split a (foo 1 2 3) in a number of ways. Yet another issue is what do you do with something like:

    (list +)
    

    Here you have + as an expression, but you could decide that this is the same as applying it on zero inputs which fits +s arity, but then how do you write an expression that evaluates to the addition function? (Sidenote: ML and Haskell "solves" this by not having nullary functions...)

    Some of these issues can be resolved by deciding that each "real" application must have parens for it, so a + by itself will never be applied. But that loses much of the cuteness of having an auto-curried language, and you still have problems to solve...

提交回复
热议问题