Case study: Multi-value return statements
Comparing C#, TypeScript and Golang

Working with different programming languages allows you to compare certain features between them. Convergence between languages, (and also frameworks) becomes noticeable: It seems new language- and framework features are “borrowed” from other language- or frameworks, or are sometimes the result of “taking the best of both worlds”. Multi-value return statements in programming languages is one such feature.

This post will focus on this concept, compared between the languages Golang, C# and TypeScript.

What are multi-value return statements anyway?

Let’s start with Golang. Golang is a (very) strict language, which forces you to write clean code. It also heavily relies on multi-value return methods, like for explicit error-handling for example.

func Hello(input string) (output string, err error) {  
  if len(input) == 0 {   
    return "", errors.New("Blanks not accepted")  
  }  
  return input + "!", nil 
}
func main() {
  out, err := Hello("Josh")
  if err != nil {
    log.Fatal(err)
  }
  // continue with 'out' parameter
}

Golang forces you to use all variables you define, otherwise, the code won’t compile. (Did I mention Golang was strict?) Which in turn forces you to really think about error-handling up-front, instead of an afterthought. (“Oh right, exception handling…”). In the case where you don’t need any of the return values, you can use the underscore to signal Golang you are not interested in this output variable:

_, err := Hello("Josh") // Just checking the error...

So why is this useful?

Explicit exception handling is just one good example, but is specific to Golang. Another, more generic, benefit is that you don’t need another ‘wrapper’ object or data-structure with the sole purpose of delivering exactly one output parameter. For example, using the explicit Tuple data structure in C#, is a thing of the past.

Multi-value return statements weren’t part of C# for a long time. But since C# 7.0, we can write something like this:

public (bool Valid, string Message) Validate(Model model)
{
    if (model.BuildingYear == null)
    {
        return ( true, "Warning - No building year supplied." );
    }
    if (model.BuildingYear >= 1000 && model.BuildingYear < 10000)
    {
        return ( true, null );
    }

    return ( false, $"Error - BuildingYear {model.BuildingYear} is not in the correct format YYYY." );
}

Calling the method, looks like this:

var (isValid, message) = Validate(myModel); 
// now you can use 'isValid' and 'message' independently.

Although C# code will still compile if you have defined unused variables, IDE’s like JetBrains Rider will advise you to rename those unused variables to underscore’s, just like in Golang’s syntax.

// Rider IDE will advice you rename 'message' 
// to '_' if this parameter is not used.
var (isValid, _) = Validate(myModel);

What about TypeScript?

With TypeScript (and also JavaScript ES6), you cannot return multiple values from a single method, like the way Go and C# are doing. Here, you mostly still use (nameless) javascript objects that are the ‘wrappers’ around multiple values you want to return. However, since ES6, the same end-result can be achieved with the use of destructuring. This gives you syntactic sugar to easily access multiple values from a return statement in a single call. Here is a TypeScript example, with the same logic as the one from the C# example:

private validate(model: Model): { valid: boolean, message: string } {
  if (model.buildingYear === undefined) {
    return { valid: true, message: 'Warning - No building year supplied.' }
  }
  if (model.buildingYear >= 1000 && model.buildingYear < 10000) {
    return { valid: false, message: '' }
  }

  return { valid: false, message: `Error - BuildingYear ${model.buildingYear} is not in the correct format YYYY.`}
}

Although we are returning a ‘wrapper’ object instead of multiple values natively, by using destructuring, we can have direct access to the individual parameters inside the object.

const { valid, message } = this.validate({ buildingYear: 1600 })
// You can now use 'valid' and 'message' independently.

Compare the statement above to calling the method in the C# example and you’ll see the syntactic resemblance immediately.

TypeScript alternative

Instead of returning a simple anonymous object and use destructuring to parse the results, you can alternatively write this even shorter (at the expense of readability): You return an array containing all values instead of object. You essentially drop the keys and only use the values now:

private validate(model: Model): [boolean, string] {
  if (model.buildingYear === undefined) {
    return [true, 'Warning - No building year supplied.']
  }
  if (model.buildingYear >= 1000 && model.buildingYear < 10000) {
    return [false, '']
  }

  return [false, `Error - BuildingYear ${model.buildingYear} is not in the correct format YYYY.`]
}

Conclusion

Programming languages keep evolving. And upon doing so, the best concepts are sometimes getting ‘borrowed’ from, or influenced by other languages, which can only be seen as a good thing!

Thanks for reading.


Last modified on 2019-03-26