Orcris
Orcris

Reputation: 3365

How do I only allow an argument in a Ruby function to be a certain type?

For example, if I have

def function(arg)
  #do stuff
end

how do I only allow arg to be an Array? I could do

def function(arg)
  if arg.class != 'Array'
    return 'Error'
  else
    #do stuff
  end
end

but is there any better way of doing this?

Upvotes: 7

Views: 2920

Answers (6)

sawa
sawa

Reputation: 168101

If the receiver in your example is trivial and it is defined like a function rather then a method, then, you might want to turn the argument into a receiver.

class Array
  def function
    # do stuff
  end
end

Upvotes: 0

peter
peter

Reputation: 42192

In stead of raising an error you could make certain you convert the argument to an array but you should rather work at the routine providing the parameter so it gives you an array in the first place.

def function(*arg)
  a = arg.flatten
  p a
end

function("ab")
function(1,2)
function([1,2])

#=>
#["ab"]
#[1, 2]
#[1, 2]

Upvotes: 0

Samy Dindane
Samy Dindane

Reputation: 18706

You can't do def function(Array arg) like in other languages, but you can replace four lines of your second snippet by a single one:

def function(arg)
  raise TypeError unless arg.is_a? Array
  # code...
end

Upvotes: 13

Todd A. Jacobs
Todd A. Jacobs

Reputation: 84353

Ruby does not support type checking--at least, not in the sense of being able to declare argument types as part of the method signature. Generally, the caller is responsible for passing the correct objects, or the passed objects are responsible for converting themselves. However, you can certainly do your own coercions. In fact, about.com has an example of using the case statement for doing just this kind of type checking.

Here are some additional examples:

# Ask your object to convert itself to an array.
def foo my_object
  my_object.to_a
end

# Give your object a #to_a method if it doesn't already have one.
def foo my_object
  unless my_object.respond_to? :to_a
    def my_object.to_a
      # perform your type conversion
    end
  end
end

# Do your own type conversions.
def foo my_object
  case my_object
  when Array
    p my_object
  when String
    my_object.scan /\d+/
  end
end

You can mix and match any of these techniques, and there are certainly others. Ruby is a very flexible language.

Upvotes: 1

DGM
DGM

Reputation: 26979

Others have pointed out how you can technically enforce a type of an argument, but you are fighting a fundamental battle with the ruby way of thinking. Google around for "duck typing".

In the ruby world, we don't usually worry about the type of an argument, but rather whether it meets the interface of what we want to do with it. In other words, does it quack like a duck? Good enough!

Lets suppose you are trying to use an Array for something:

def function(arg)
  arg.each do |a|
    puts a
  end
end

If you call this like: function(1), you get NoMethodError: undefined method 'each'

Let's use your idea of forcing it to be an array.

def function(arg)
  raise TypeError unless arg.is_a? Array
  arg.each do |a|
    puts a
  end
end

Ok, now we have made a different error, but if the method gets called with something other than an array, it still generates an error.

Furthermore, what about this?

function("Hello World".chars)

Whoops, we have an error, despite the fact that the function would have worked!

The ruby way would be something more like:

def function(arg)
  if arg.respond_to? :each
    arg.each do |a|
      puts a
    end
  else
    puts arg
  end
end

Now your function works well for all sorts of inputs:

function([1,2,3,4])
function("foo")
function("foo".chars)

As for the correctness of the function, you cover that using tests, not the compiler. But that's a whole other subject. :)

Upvotes: 7

Kevin Sjöberg
Kevin Sjöberg

Reputation: 2254

Well considered that Ruby is dynamically typed you can't enforce that in a similar way such as in C. Instead of comparing against a string you can actually compare it against the class.

arg.is_a?(Array)

To improve your example I would have written:

 def function(arg)
   raise TypeError unless arg.is_a?(Array)
   # Do stuff
 end

Upvotes: 2

Related Questions