Skip to content
Tom Johnell edited this page Oct 11, 2016 · 8 revisions

What return contract should initialize have?

It's recommended that your initialize method return C::Any. It doesn't make much sense to return self and explicitly define a contract to return Foo because the return value of initialize does not affect the return value of the class method Foo#new (unless you override the class method new which you very likely won't be doing). Meaning, no matter the return value of initialize you can do something along the lines of Foo.new.bar.

class Foo
  Contract nil => C::Any
  def initialize
    @bar = 1
  end
      
  def bar
    @bar
  end
end

It's not obvious what a class setter should return

Again, it will return the value of the last statement, but you might want to return the object instead so you can chain method calls:

Contract String => String
def bar=(val)
  @bar = val
end

It's not obvious if a function argument which is assigned a default value in the signature should be contracted as String, Or[String, nil], or Maybe[String]

Probably you would like String (this will catch the most bugs):

Contract String => nil
def hello(name="adit")
  p "hello, #{name}!"
end

hello

But if you need to support the case where you accidentally get passed an actual nil value, you'll have to do something like this:

Contract Maybe[String] => nil
def hello(name="Adit ")
  name = "" if name.nil?
  p "hello, #{name}!"
end

hello(nil)

Maybe makes it extremely clear that we're capable of handling nil cases. Or was created to handle cases where we're handling two non-nil types: Or[String, Num].

How to specify a fixed length array argument?

Use Array#*. For example, to specify an array of 5 Strings:

Contract(([String] * 5) => Any)

Which Array#* expands to

Contract [String, String, String, String, String] => Any