The Point of No Return

By
at May 14th, 2012

What a Ruby function looks like to a mind full of Java method

If you’re used to Java, your methods let everyone know if you’re going to return something, and what kind of thing you are going to return. I also like to think you mark out when and where you will return it with a return statement.

Ruby has no such restrictions.

Take this:

def areUreturning
  6 % 2
end

Calling areUreturning returns zero.  That’s it. The value of the last statement executed comes back. I suppose this saves you typing six letters in your class over and over, but it sends warning bells to me about accidentally returning the wrong value.

And then there’s this:

@scopedVariable
def updateGlobal(result)
  @scopedVariable += result
end

def addTwo(result)
  #do something
  result +=2

  #update someone else
  updateGlobal(result)
end

The intention of the programmer here was to return result+2, but when it goes off to update the scopedVariable, it’s going to return the last line of that method instead. The old Java brain may look at updateGlobal and assume it won’t return anything as there is no return type and it’s safe to do this.

In order to get the intended value, you have to make sure that value is the last thing in the method.

def addTwo(result)
  #do something
  result +=2
  #update someone else
  updateGlobal(result)
  result
end

Weird right?  Just typing a variable out at the end?  So here’s my first chance to empty a few drops. Now, you can type return if you want, and in fact use it like a break statement to return control to the caller. One thing I’ve learned going through this exercise, is to assume everything returns something.

Void is in the void. Keeping this in mind, it starts to make sense that variables are just lying around at the end of some of these methods. If you get used to reading a method from the last end upwards, you’ll get used to making sure you’re returning the right value.

This next bit is about as dynamic as I’ve seen. You can pass in anything and get anything back.

You see, Ruby functions take an optional array at the end that can contain anything you want.

It’s denoted by the lovingly-named splat operator * formerly knows as the asterisk

array1 = ['1', 'Integer']
array2 = ['2', 'string']
def giveMeAnything(*arr)  // <-- The splat
  @b
  case  arr[0]
  when "Integer"
    @b = 1
  when "string"
    @b = '2'
  end
  @b
end

So, giveMeAnything(array1) returns an Integer, giveMeAnything(array2) returns a String.

In java I would have a returnFieldAsString method and a returnFieldAsInteger method defined for whatever object I was working on.  I would ask for a specific type and rest confidently that I was getting what I asked for.

So how do we get past this one?

We are working in a black box now. These functions can’t be trusted, so you have to protect yourself before calling, and there is no compiler to save you.  So, you’ll have to pay strict attention to what the function does.

There are type-checking paradigms in Ruby. You can inspect the object for the type, or use the handy kind_of? method to be sure:

def onlyTakesString(arg)
  raise "arg must be a String" unless arg.kind_of? String
  #do something
end

def onlyReturnsString(arg)
  @localString = "String"
  #do something
  raise "must return a String" unless @localString.kind_of? String
end

But this seems like it’s very much not the point of using Ruby. So the next step of this mental migration is to pay attention to what a function is doing and stop relying on types to save you.  One other paradigm, if you really want to keep the function polymorphic is to pass in the type you want returned.

returnValue(arg1,arg2="String)

I was kind of doing that in the example. Otherwise, name your function after what it does the way you are used to in java:  return_me_a_string(arg1)

tl;dr

You don’t declare a return type, or need to explicitly return a value.

Each function returns the result from the last line executed, which can be of any type.

Non-Ruby thing you will most likely do:

use return statements

use kind_of? to check types

New Ruby Habit

Look at the bottom of a function to see what’s being returned, instead of the top.

Think of functions as being polymorphic  instead of the objects passed to them.

Be vigilant and make no assumptions about a function.

Name the functions with a hint of their return type in the name, or pass in a return type you want back.

No Tags