Tuples and functions

Introduction

Tuples are very versatile in Modern Eiffel. This is due to the possibility
that tuples and sequences of expressions can be freely mixed as long as the
corresponding types match.

In the following tuples and the possible use of tuples in expressions and
functions is shown.

Tuples

Tuples are product types. If you have the types A and B, then the type [A,B]
is a tuple where the first component is of type A and the second is of type
B.

You can construct tuples with more components as [A,B,C,…].

The special case of an empty tuple type [] (i.e. a tuple with no component) is
possible as well.

If “e” is an expression of type [A,B,…] you can access the components by
“e.1”, “e.2”, …

A tuple object of type [A,B] can be created with the expression “[a,b]” where
“a” is an expression of type A and “b” is an expression of type B.

Tuple types are immutable. Two objects of tuple type are equal if and only
if their corresponding components are identical.

In order to access the components by name it is possible to label the tuple
type. The type [a:A,b:B] is semantically the same type as [A,B]. If “e” is an
expression of type [a:A,b:B] then the components can be accessed by “e.a” and
“e.b”.

  -- tuple type
  [A,B]     -- tuple type with two components
  [a:A,b:B] -- labelled tuple type
  []        -- empty tuple type
  

  [a,b]     -- expression of tuple type [A,B], 
            -- if "a" has type A and "b" has type B


  [a,b].1 ~ a   -- component access
  [a,b].2 ~ b   -- Note: the identity operator has to be used, because
                --       A and B might be mutable

A tuple of type [A,B,…] is semantically equivalent to the inductive type

  case class SOME_TUPLE[A,B,...] create
    tuple(a:A; b:B; ...)
  end

i.e. the proof engine infers automatically the rules for equality

  all(x,y:SOME_TUPLE[A,B,...]) check
    elim_1:  (x=y) => (x.a~y.a)
    elim_2:  (x=y) => (x.b~y.b)
    ...
    intro:   (x.a~y.a) => (x.b~y.n) => ... => (x=y)
  end

Nesting and conversion of tuples

Tuples can be nested to arbitrary depth.

  [A,B]
  [A,[B,C],D]
  [[[A]]]

For each tuple type there is a corresponding sequence of types which is
obtained if all square brackets are ommitted

  tuple type                            sequence of types  
  [A,B]                                 A,B
  [A,[B,C],D]                           A,B,C,D
  [[[A]]]                               A

An object of tuple type T1 can be converted into an object of tuple type T2 if
the corresponding sequences of types are identical.

All of the following tuples are mutually convertible.

  [A,B,C,D,E]
  [[A,B],C,D,E]
  [A,[B,C],D,E]
  [A,[B,[C,D]],E]
  ...

Furthermore any sequence of expressions can be converted into a tuple and vice
versa as long as the sequence of their types is identical to the sequence of
types of the tuple.

These conversion rules give enormous flexibility with function calls and
variable initializations. This will be demonstrated with some examples in the
following chapters.

Function calls with tuples

Consider a function with the declaration

  f(a:A, b:B, c:C, ...): RT

then

  f(a,b,c,...)

is a valid expression if the expression “a” is of type A and the expression
“b” is of type B …

Lets assume further that we have a function “g” returning an object of tuple
type [A,B] i.e. it has the signature

  g(x:X): [A,B]

In this case the expression

  f(g(x),c,...)

is valid as long as “x” is an expression of type “X”.

I.e. a tuple can replace a sequence of arguments in expressions as long as the
types match.

Variable initialization with tuples

Modern Eiffel has let expressions (i.e. expressions with local variables)

  local
     a:A, b:B, c:C,... := aexp, bexp, cexp
  then
     f(a,b)
  end

with the obvious meaning.

Any sequence of expressions can be replaced by a tuple expression with
consistent types. With the above function “g” which returns a tuple of type
[A,B] the following is valid as well.

  local
     a:A, b:B, c:C,... := g(xexp), cexp
  then
     f(a,b,c,...)
  end

Tuple initialization with variables

The last two chapters have shown that a tuple expression can initialize a
sequence of variables as long as the corresponding types match.

The reverse is useful as well. We can call functions which expect tuple
arguments with a corresponding sequence of expressions.

  -- signature of function "h"
  h(a:A, t:[X,Y], b:B): RT

  -- valid expressions
  h(aexp, [xexp,yexp], bexp)

  h(aexp, xexp, yexp, bexp)

The same applies to tuple initialization in let expressions

  local
    t:[X,Y] := xexp, yexp
  then
    ...
  end

Functions with multiple return values

A function returning a tuple type returns implicitly multiple values. E.g.

  tup_func(x:X): [A,B]
    require
      ...
    ensure
      Result = [aexp, bexp]
    end

In Modern Eiffel we can define a function which explicitly returns multiple
values.

  mr_func(x:X): a:A, b:B
    require
      ...
    ensure
      a = aexp
      b = bexp
    end

With that definition the function “mr_func” can be used in exactly the same
places as “tup_func”.

Advertisements
  1. Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: