Click here to Skip to main content
Click here to Skip to main content

Using Tuples to Synthesize Polyadic Returns in C# and COBOL

, 18 Jan 2010
Rate this:
Please Sign up or sign in to vote.
The introductions of Tuples (originally to support F#) into .NET shows a possible way to use polyadic returns in C#, COBOL etc., without syntax enhancement.

Introduction

As I have mentioned before, the lack of polyadic returns from many supposedly modern languages acts a a barrier to their future usefulness. However, the introduction of Tuples (originally to support F#) into .NET shows a possible way to retrofit this feature without syntax enhancement. I demonstrate the technique in C# and COBOL.

Why do we have function signatures like this:

void divideAndRemainder(int numerator, int denominator,ref int div, ref int remain)

The result of the function is obviously polyadic, it has two distinct parts. Because our programming language is not set up to handle polyadic returns, we pass in parameters by reference to act as return values. This is a big fat hack. One or the other, but not both please. What would be great is something like this:

void (int ref, int remain)divideAndRemainder(int numerator, int denominator)

But we cannot have that in C#, VB, Java, COBOL ....

In F#, we do have native syntax for tuples:

let someFunc= (1,"hello world")

The above will produce a strongly typed tuple which contains two items: the first an integer, and the second a string. The function is polyadic. Not only that - but it is implemented on the Common Language Runtime. So, it must be possible to access whatever structures F# is using from other .NET languages, and it is. OK, the result is a bit clunky, but it is a first step.

To illustrate, I will use a very simple example: a function to divide an integer by an integer and get the result and remainder. This example shows two huge advantages of the tuple approach.

  1. It gets rid of the need for reference parameters to synthesize polyadic returns.
  2. It allows for in-line encapsulation of data by functions, making the role of variables less complex in code, and thus making the code closer to a functional description of what it is actually doing (more self documenting).

Here is the classic C# approach to the example:

    ...
    // Example 4: The horrid way
    int local2 = 26;
    int local3 = 4;
    int local4 = 0;
    var local5 = divAndRemainderHorrid(local2, local3,ref local4);
    System.Console.WriteLine
    (
            "Dividing " + local2 +
            " by " + local3 +
            " gives " + local5 +
            " remainder " + local4
    );
    ...
    static int divAndRemainderHorrid(int numerator, 
               int denominator, ref int remainder)
    {
        remainder = numerator % denominator;
        return numerator / denominator;
    }
}

There is nothing exactly wrong with this; it is just a mess. The function returns some of its results and passes back some of it via a reference variable. I guess one could make the function void and pass back everything via reference parameters - but that does not seem like much of an improvement. Also, there has to be heavy use of locals to manage all the intermediate storage. This makes chaining of the function near impossible. E.g., we could not make a function WriteResult which encapsulates the System.Console.Write line code and call that with the return from the divide/remainder function without marshalling all the intermediates via local variables.

Doing it with Tuples

F# tuples are implemented under the covers using System.Tuple. This type has a set of generic factor methods Create(...) which allow the generation of strongly typed tuples of up to 7 elements. It is possible to make longer tuples by chaining them (putting tuples in tuples). F# does this chaining for us; however, in other languages, this has to be done by hand, which makes longer tuples rather clunky. See http://msdn.microsoft.com/en-us/library/system.tuple(VS.100).aspx.

Rather than write lots of text describing tuples further, I have illustrated their use via code.

Example 1 shows a simple example of using tuples so that dividend and remainder are returned in one tuple, thus avoiding the use of a reference parameter. Example 2 shows how we can make the function return a tuple of the passed in parameters and the results so that all the information about the operation is encapsulated. This then allows for simple access to this encapsulated information.

Example 3 is the really interesting one. It shows that by fully encapsulating all the information about a function, it is possible to dispense with local temporary storage (variables) altogether. Example 3 shows how using tuples has allowed us to write code which exactly expresses the actions being performed without having to have loads of operational clutter getting in the way!

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Tuples
{
    class Program
    {
        static void Main(string[] args)
        {
            // Example 1: Avoiding reference types
            System.Console.WriteLine("Result=" + DivAndRemainder (10 , 3));

            // Example 2: Avoiding reference types and encapsulation
            var local1 = DivAndRemainderKeepAll(26, 4);
            System.Console.WriteLine
            (
                    "Dividing " + local1.Item1 +
                    " by " + local1.Item2 +
                    " gives " + local1.Item3 +
                    " remainder " + local1.Item4
            );

            // Example 3: Function chaining
            WriteResult(DivAndRemainderKeepAll(26, 4));

            // Example 4: The horrid way
            int local2 = 26;
            int local3 = 4;
            int local4 = 0;
            var local5 = DivAndRemainderHorrid(local2, local3,ref local4);
            System.Console.WriteLine
            (
                    "Dividing " + local2 +
                    " by " + local3 +
                    " gives " + local5 +
                    " remainder " + local4
            );
        }

        static Tuple<int, int> DivAndRemainder(int numerator, int denominator)
        {
            return Tuple.Create(numerator / denominator, numerator % denominator);
        }

        static Tuple<int,int,int, int> 
               DivAndRemainderKeepAll(int numerator, int denominator)
        {
            return Tuple.Create
            (
                numerator,
                denominator,
                numerator / denominator,
                numerator % denominator
            );
        }

        static int DivAndRemainderHorrid(int numerator, 
                   int denominator, ref int remainder)
        {
            remainder = numerator % denominator;
            return numerator / denominator;
        }

        static int WriteResult(Tuple<int, int, int, int> result)
        {
            System.Console.WriteLine
            (
                "Dividing " + result.Item1 +
                " by " + result.Item2 +
                " gives " + result.Item3 +
                " remainder " + result.Item4
            );
        }
    }
}

Doing it with COBOL

Micro Focus COBOL for .NET has full support for generics with a simply syntax. This allows us to use Tuples in a straightforward way:

procedure division 
using by value numerator as 
binary-long denominator as binary-long 
returning result as 
type System.Tuple[binary-long binary-long binary-long binary-long].

Above, we can see the signature definition for the DivAndRemainderKeepAll method. This is equivalent to the C#:

static Tuple<int,int,int, int> DivAndRemainderKeepAll(int numerator, int denominator)

Which is 'better' is simply a question of taste. Also, we can note that they are fully interoperable. So the whole of example 3 in COBOL is here:

class-id. "Tuples".

   method-id. Main static.
   procedure division.
       invoke Tuples::WriteResult
       (
           Tuples::DivAndRemainderKeepAll(26 4)
       )
   end method.
   
   method-id. DivAndRemainderKeepAll static.
       01 div binary-long.
       01 rem binary-long.
   procedure division 
       using by value numerator as 
       binary-long denominator as binary-long 
       returning result as 
       type System.Tuple[binary-long binary-long binary-long binary-long].
           compute div = numerator / denominator
           compute rem = function mod(numerator denominator)
       set result to type System.Tuple::Create(numerator denominator div rem)
   end method.
   
   method-id. WriteResult static.
   procedure division 
       using by value result 
       as type System.Tuple[binary-long binary-long binary-long binary-long].
       invoke type System.Console::WriteLine
       (
           String::Format
           (
               "Dividing {0} by {1} gives {2} remainder {3} "
               result::Item1 
               result::Item2 
               result::Item3 
               result::Item4 
           )
       )
   end method.
end class.

Multi-Core and the Cloud - Why this Really Matters

So far, I have presented some nice syntactic reasons for using Tuples specifically, and polyadic returns in general. There is a really major, huge, looming and life critical reason why we - as developers and architects - need to start thinking this way. Hardware is changing - processing distribution is ascendant!

The problem is that passing in reference parameters just to allow polyadic return does not actually express the intent of the programmer. The compiler and runtime must understand the reference parameter has a value which exists outside the scope of the function to which it is passed. It can therefore be updated outside that scope, and updates inside that scope should be visible outside it. These scoping effects are not the intention of the programmer, but the compiler and runtime do not 'know' this.

In a massively multi-core or distributed cloud environment, we might want to execute a function on a different core from the one on which it is called. If the function does not reference any external storage (class, object, or calling function local variables), then there is no reason this cannot be done. However, the reference parameters mean that the compiler and runtime 'think' the function does reference external storage and so they cannot efficiently move execution onto a different core.

Conclusions

The tuples based patterns used here show how to overcome a major drawback of many traditional programming languages (the lack of polyadic returns) and so pave the way to more efficient program implementations in current and upcoming environments. Where we can expect languages such as COBOL and Algol's children (C#, Java etc.) to continue to do the heavy lifting of much of the computing, these techniques could prove a very useful way forward until such time as explicit polyadic types are added into the syntaxes themselves.

License

This article, along with any associated source code and files, is licensed under The Creative Commons Attribution-ShareAlike 2.5 License

About the Author

alex turner
Web Developer
United Kingdom United Kingdom
I am now a Software Systems Developer - Senior Principal at Micro Focus Plc. I am honoured to work in a team developing new compiler and runtime technology for Micro Focus.
 
My past includes a Ph.D. in computational quantum mechanics, software consultancy and several/various software development and architecture positions.
 
For more - see
 
blog: http://nerds-central.blogspot.com
 
twitter: http://twitter.com/alexturner

Comments and Discussions

 
GeneralPolyadic or Polygot PinmemberRichard Ashman26-Jan-10 2:25 
Hi Alex,
 
For the sake of clarity for the reader :-
 
The arity of a function describes the number of arguments it takes. A function with polyadic arrity takes any number of arguments.
 
In the functional languages with which I am familiar, the tuple type is an immutable linked list of pairs and thus representative of small sets of structured data. They may very well be used to represent the set of arguments provided to or returned from a function, but these languages also support first class concepts such as identity, purity, immutability, higher order functions and currying .. with monads providing the necessary glue.
 
In the object orientated languages with which I am familiar, small sets of data are often represented by complex types (classes or structures) or simpler/jagged arrays, and the compilers/developers are highly tuned to work with such constructs. Furthermore mutability, the exception model, reference types etc are all embraced as powerful concepts, alongside inheritence, polymorphism etc.
 
They are fundamentally different beasts with different audiences and different targeted problem domains.
 
While languages such as C# have added more 'functional' capabilities in recent years, it remains at its core an OOP language and both suffers/benefits in differing measures dependent upon the problem domain it is applied to. Using the wrong tool for a job does not mean the tool is of no use .. it was simply designed for another purpose.
 
In my humble opinion, to describe C#'s lack of a first class polyadic support as a barrier to future usefulness is to massively overstate a non-existent problem. If you value polyadicity over polymorphism, you should not be using C#.
 
OOP languages don't need to support tuple in a first class manner (and I am a strong proponent of list based languages in the right circumstances). Since generics were introduced to the .NET languages, we've been able to roll our own anyway ... but I wonder how many people have ventured to do so and build API's dependent on them.
 
I suspect their introduction into .NET 4 is purely to simplify interoperability with programs written in functional languages such as F# and for no other reason.
 
-------
 
In this article you investigate the usefulness of such a type for returning multiple results from a function to mimic polyadicity instead of using out/ref parameters, yet I would have to agree fully with chrwal. Ref parameters are not the only option and I'd suggest a simple value type (over a tuple) would carry less overhead and be easier for the consumer to reason against as it would contractually expose named properties which were specific to the domain of the operation (eg result.Remainder). If we substitute a tuple, there is no ability to reason which element contains which part of the result .. be I a human or a compiler.
 
Furthermore I think you have failed to guide the reader to consider the true value of a function with a polyadic list of results .. enabling functions to compose together, ignoring and consuming elements of the result as they see fit, yet allowing them to propogate down the pipeline invisibly ... though in all fairness, that's more of a functional proposition.
 
I also don't understand your claims that in order to successfully write massively scalable, multi threaded applications for the 'cloud' we must adopt such constructs. I'm sure many of us have been successfully architecting software with such characteristics long before grids and meshes became virtualised clouds ... though we probably haven't been using C#.
 
I also disagree with your point about how compilers can statically reason about code and intent. There are many barriers in the way of an OOP language compiler which prevent it from been able to statically reason about the programmers intent, but ref parameters is surely not a big one. Lisp/Erlang compilers can reason at a far deeper level and schedule/coordinate threads transparently (map reduce) because they are pure languages (ie side effect free). I am sure there are many clever people as we speak inside Microsoft who have been reasoning about the oncoming multi-core onslaught for many years. Linq, Workflows, Parallel Extensions, Transactional Memory, Linear Types are all proof that they are thinking, reasoning, tooling and believe they can abstract much (if not all) of the complexity away from the developer.
 
In the meantime, there are hundreds of different languages out there for us to choose from .. so surely we should all try to become true polygots?
GeneralRe: Polyadic or Polygot Pinmemberalex turner26-Jan-10 2:42 
GeneralRe: Polyadic or Polygot PinmemberRichard Ashman26-Jan-10 4:29 
GeneralRe: Polyadic or Polygot Pinmemberalex turner26-Jan-10 4:40 
GeneralDIVIDE (not the main issue of the article) PinmemberPablo Aliskevicius25-Jan-10 23:08 
GeneralRe: DIVIDE (not the main issue of the article) Pinmemberalex turner25-Jan-10 23:15 
GeneralIt's .NET 4.0 beta PinmemberMollyTheCoder25-Jan-10 17:50 
GeneralRe: It's .NET 4.0 beta Pinmemberalex turner25-Jan-10 23:07 
GeneralNice article PinmentorNicholas Butler19-Jan-10 2:03 
GeneralYou could have returned a struct Pinmemberchrwal18-Jan-10 11:36 
GeneralRe: You could have returned a struct Pinmemberalex turner18-Jan-10 22:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140718.1 | Last Updated 18 Jan 2010
Article Copyright 2010 by alex turner
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid