2

In functional languages without type checking, is there any substantial disadvantage (apart from readability) to limiting all functions to take exactly one argument - that is, replacing multi-argument functions with functions that take a tuple as an argument?

(I saw some comments about it being somewhat more difficult to design the type system when functions accept tuples. I am interested in considerations unrelated to this issue.)

1
  • 1
    Multiple arguments, used in moderation, can make it more apparent which are required and which are optional. Commented Jan 21, 2017 at 22:50

2 Answers 2

5

There is indeed a fundamental disadvantage to using a tuple, versus having multiple parameters: you lose the ability to use partial applications and currying.

Partial function application and currying are central features to functional languages. The fact that all functions are represented as a series of one-parameter functions that call each other, allows parameters to be "applied", eg in the following example, double is defined as a partially applied multiply function:

let multiply x y = x * y    
let double = multiply 2
let num1 = 10
let num2 = double num1

Replace x y with a tuple, (x, y) and suddenly the power of partial applications is removed. This is a very real issue in F#, which curries all functions. As a result, to enable interoperability with other .NET languages, the parameters for methods of all non-F# assemblies are treated as tuples. This means that partial application of those methods is not possible.

4
  • On the other hand, sometimes you want to use a product type precisely to prevent currying, in situations where the partially applied function would be meaningless. For a (somewhat contrived) example, consider a function complexNorm which computed the norm of a complex number, which are represented by a real and imaginary component. It wouldn't make sense to pass just the real value first to the function and obtain a partially applied function expecting an imaginary component. Commented Jan 22, 2017 at 17:18
  • 1
    Of course, many functional languages that don't auto-curry all function definitions do have convenient syntax to perform partial application, e.g. f (x, _) or f (x, *). It can still be done manually with a lambda \y -> f (x, y). Given a sufficiently powerful type system (e.g. one that defines tuples recursively), it's also possible to create a function to curry functions. Commented Jan 22, 2017 at 17:21
  • @amon so only auto-currying becomes impossible, while currying and partial applications can still be done with a little fancier syntax? Commented Jan 22, 2017 at 18:44
  • @max yes of course. For example if we have def f(a, b, c) in Python, we can turn it into a curried function like lambda a: lambda b: lambda c: f(a, b, c). We could partially-apply just b=42 like lambda a, c: f(a, 42, c). Any language can do this as long as it has some form of closures. Single-argument functions are occasionally an annoyance, but they do not fundamentally constrain the language. Auto-currying as in Haskell is needed though to program in point-free style (avoiding explicit arguments), which is sometimes more elegant. Commented Jan 22, 2017 at 18:57
3

The author of that blog post seems to be ignoring that most languages with generic types and subtypings have some way of expressing variance in their generic parameters. They write:

So here's the issue: if we have a function that accepts arguments of type Tuple, and we want to pass it a tuple of arguments of type Tuple, we can only do this when A = X, and B = Y. If X is a subtype of A or Y is a subtype of B, we will get a type error. Subsumption is broken. Suddenly, you can't pass a String as an argument to a function that accepts Objects.

But actually, a tuple can be defined as something like:

case class Tuple2[+T1, +T2]

in Scala and other similar languages (C# or Java) even if it isn't always defined so, which gives it the proper variance.

Even though this is not a problem there are some tradeoffs between the two:

Pro multiple arguments:

  • Makes optional, named and variadic arguments quite straightforward.
  • Slight possible performance benefit (no tuples have to be created).
  • Can have as many arguments as you want, without the language having to provide up to Tuple20 say.

Pro tuples:

  • Tuples are themselves values that can be passed around and transformed.
  • Requires the language to support a nice tuple syntax to be usable.

As David Arno has pointed out, both tuples as single arguments and multiple arguments have the same disadvantage against fully curried arguments: they cannot be partially applied.

Edit:

So I might have misread the question, though I think it's vague on this point. For clarity, there are three ways a function might "take multiple arguments". The first is by actually taking multiple arguments, which is what the majority of programming languages do. For example, in Scala,

def concatenateMulti(left: String, right: String): String = left + right

the second is to take a single argument that is a tuple or record, e.g.

def concatenateTuple(args: Tuple2[String, String]): String = args._1 + args._2

and the third is to take a function that returns a partially applied function

def concatenateSingle(left: String): (String => String) = (right: String) => left + right

I assumed the author was asking after the first versus the second whereas it seems they were more interested in the second versus the third

3
  • Could you clarify the distinction between tuples as single arguments and as multiple arguments? If I understand correctly, my original question is about tuples representing multiple arguments, and I understand why the simple currying wouldn't work in that case. But what is the other case you're referring to? Commented Jan 22, 2017 at 18:43
  • @max, does my edit address it? Commented Jan 22, 2017 at 19:17
  • Yes, and I did mean (1) vs (2) in my question. Commented Jan 22, 2017 at 19:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.