I am thinking about making currying and variadic functions both available in a dynamically-typed functional programming language, but I wonder if it is possible or not.
Here are some pseudocode:
sum = if @args.empty then 0 else @args.head + sum @args.tail
which is supposedly to sum all its arguments. Then, if sum
itself is treated a number, then the result is 0
. for example,
sum + 1
is equal to 1, assuming that +
can only work on numbers. However, even sum == 0
is true, sum
will still maintain its value and functional property no matter how many arguments are given (hence "partially applied" and "variadic" at the same time), for example, if I declare
g = sum 1 2 3
then g
is equal to 6
, however, we can still further apply g
. For example, g 4 5 == 15
is true. In this case, we cannot replace the object g
by a literal 6
, because although they yield the same value when treated as an integer, they contain different codes inside.
If this design is used in a real programming language, will it cause any confusion or ambiguity?
Best Answer
How can varargs be implemented? We need some mechanism to signal the end of the argument list. This can either be
Both of these mechanisms can be used in the context of currying to implement varargs, but proper typing becomes a major issue. Let's assume that we are dealing with a function
sum: ...int -> int
, except that this function uses currying (so we actually have a type more likesum: int -> ... -> int -> int
, except that we don't know the number of arguments).Case: terminator value: Let
end
be the special terminator, andT
be the type ofsum
. We now know that applied toend
the function returns:sum: end -> int
, and that applied to an int we get another sum-like function:sum: int -> T
. ThereforeT
is the union of these types:T = (end -> int) | (int -> T)
. By substitutingT
, we get various possible types such asend -> int
,int -> end -> int
,int -> int -> end -> int
, etc. However, most type systems do not accommodate such types.Case: explicit length: The first argument to a vararg function is the number of varargs. So
sum 0 : int
,sum 1 : int -> int
,sum 3 : int -> int -> int -> int
etc. This is supported in some type systems and is an example of dependent typing. Actually, the number of arguments would be a type parameter and not a regular parameter – it would not make sense for the arity of the function to depend on a runtime value,s = ((sum (floor (rand 3))) 1) 2
is obviously ill-typed: this evaluates to eithers = ((sum 0) 1) 2 = (0 1) 2
,s = ((sum 1) 1) 2 = 1 2
, ors = ((sum 2) 1) 2 = 3
.In practice, none of these techniques should be used since they are error-prone, and don't have a (meaningful) type in common type systems. Instead, just pass a list of values as one paramter:
sum: [int] -> int
.Yes, it is possible for an object to appear as both a function and a value, e.g. in a type system with coercions. Let
sum
be aSumObj
, which has two coercions:coerce: SumObj -> int -> SumObj
allowssum
to be used as a function, andcoerce: SumObj -> int
allows us to extract the result.Technically, this is a variation of the terminator value case above, with
T = SumObj
, andcoerce
being a un-wrapper for the type. In many object-oriented languages, this is trivially implementable with operator overloading, e.g. C++: