1 Replies - 417 Views - Last Post: 21 July 2014 - 09:05 PM

#1 Lemur  Icon User is offline

  • Pragmatism over Dogma
  • member icon


Reputation: 1361
  • View blog
  • Posts: 3,433
  • Joined: 28-November 09

Static Typing Ruby

Posted 18 July 2014 - 09:47 PM

Oh the evil you can pull off... I'll be heavily commenting this code to explain exactly what I'm doing here. This was inspired by a chat with a friend on his decorators pattern for objects, I just wanted to take it a step further.

Friends variant: https://gist.github....b4688cbb7575106

Fair warning, this'll only work in classes. I still need to find a way to bind globally namespaced items...

def static_typed(types={}) # We're accepting a hash
  # But we only want the first value here, and first gives us an array.
  # So we end up with something like [Integer, :method]
  type, method_name = types.first

  # We get a reference to the original method
  old_method = instance_method(method_name)

  # We define a new method based on the old one
  define_method(method_name) do |*args, &block|
    old_method
      .bind(self) # We bind this new method to the old one
      .call(*args, &block) # ...and call it with the args/block passed to the new method
      .tap { |return_value| # Then we tap into the return value
        # We raise an error unless the return type is a member of the type we defined above
        # === is frequently overloaded for membership, which includes regex as well as types
        raise TypeError, "Return value is not of type #{type}!" unless type === return_value
      }
  end
end




Now what does this allow you to do?
[8] pry(#<Foo>)> class Foo
[8] pry(#<Foo>)*   static_typed Integer => def foo(i)
[8] pry(#<Foo>)*     i
[8] pry(#<Foo>)*   end
[8] pry(#<Foo>)* end  
=> :foo
[9] pry(#<Foo>)> Foo.new.foo(1)
=> 1
[10] pry(#<Foo>)> Foo.new.foo('s')
TypeError: Return value is not of type Integer!
from (pry):157:in `block (2 levels) in static_typed`
 
# Though you can be even more sneaky:
[14] pry(#<Foo>)> class Bar
[14] pry(#<Foo>)*   static_typed /[A-Z]+/ => def baz(s)
[14] pry(#<Foo>)*     s
[14] pry(#<Foo>)*   end
[14] pry(#<Foo>)* end  
=> :baz
[15] pry(#<Foo>)> Bar.new.baz('s')
TypeError: Return value is not of type (?-mix:[A-Z]+)!
from (pry):174:in `block (2 levels) in static_typed`
[16] pry(#<Foo>)> Bar.new.baz('S')
=> "S"



Though I would not recommend using this on production code at all, this is hacky business.

Is This A Good Question/Topic? 0
  • +

Replies To: Static Typing Ruby

#2 Lemur  Icon User is offline

  • Pragmatism over Dogma
  • member icon


Reputation: 1361
  • View blog
  • Posts: 3,433
  • Joined: 28-November 09

Re: Static Typing Ruby

Posted 21 July 2014 - 09:05 PM

It should be noted that this will only properly work in Ruby 2.1 and above, as that was the point that methods started returning their names as symbols after being created.

def foo() 1 end # returns :foo


Was This Post Helpful? 0
  • +
  • -

Page 1 of 1