Skip to content

Functions and tuples

tim-hardcastle edited this page Sep 9, 2023 · 14 revisions

Introduction

The tuple holds a special place in the type system. As we have seen, it is constructed simply by concatenating values with commas: 1, true, "walrus" is a tuple. We can surround it with parentheses: (1, true, "walrus"), or we can emphasize the fact that it is a tuple with the tuple keyword and call it tuple(1, true "walrus"), but these all refer to the same thing.

Tuples as function arguments

When passed to a function, tuples split up into their component pieces.

If this wasn't true, it wouldn't be possible to pass multiple values to a function at all! Consider the following function, one several example functions defined in examples/tuples.ch

swap (x, y) : y, x

When we call (e.g.) swap 1, 2, then from the definition of a tuple above, we are in fact passing it the tuple 1, 2. But tuples break up into their component parts when you pass them to functions, and so the 1 gets assigned to x and the 2 gets assigned to y.

There is no way to stop a tuple from deconstructing itself like this. If you want a type that doesn't do that, you should use a list.

Tuples as function parameters

You can, however, gather up any number of arguments into a single tuple parameter by giving the type of that parameter as tuple.

(Recall in the following example that you use arity and not len to get the length of a tuple.)

rotateLeft(t tuple) : 
    t == () : 
        ()
    else : 
        t[1::arity t] , t[0]

The tuple-typed parameter of a function need not be the only parameter, but in the present implementation of Charm it must be the last one.

For example, this works just fine:

Direction = enum LEFT, RIGHT

rotate(d Direction, t tuple) : 
    t == () : 
        ()
    d == RIGHT : 
        t[arity(t) - 1] , t[0::arity(t) - 1]
    else : 
        rotateLeft t

But if you put the parameters the other way round then Charm would throw an error.

Tuples as function return values

Again, the very definition of a tuple means that if you return more than one value from a function, you are in fact still returning one value, namely one tuple! You can't prevent that from happening either.

But when you put that together with the way tuples deconstruct themselves when passed to a function, you find that 99% of the time the net result is that you don't have to think about tuples or know that they exist. For example, if you run examples/tuples.ch, you will find that it will interpret an expression like rotateLeft ((swap 1, 2) , (swap 3, 4)) correctly without you having to remember that tuples are a type. This is why tuples are like that.

The one case where you do have to remember that tuples are a type is when you've written a function to return multiple values and then you find a circumstance where you want to call the function but then consume just one or two of those values rather than all of them. At this point it's useful to remember that your multiple return values are in fact a tuple, and to start indexing and slicing it.

Clone this wiki locally