tupl-tufts / rdl

Types, type checking, and contracts for Ruby
BSD 3-Clause "New" or "Revised" License
602 stars 38 forks source link

Structural typing with #[] #62

Closed rmosolgo closed 7 years ago

rmosolgo commented 7 years ago

I want a duck type for hash-like things, anything that responds to #[](String), but it gives a parse error:

require "rdl"

class A
  extend RDL::Annotate

  type("([[]: (String) -> %any]) -> %any")
  def b(arg)
    arg.fetch("str")
  end
end

puts A.new.b({"str" => 1})

Output:

$ ruby rdl_test.rb
rdl_test.rb:3:in `<main>':  (Racc::ParseError)
parse error on value ":" (COLON)

But, switching for fetch works:

require "rdl"

class A
  extend RDL::Annotate

  type("([fetch: (String) -> %any]) -> %any")
  def b(arg)
    arg.fetch("str")
  end
end

puts A.new.b({"str" => 1})

Output:

$ ruby rdl_test.rb
1

Is there a better way to define a type who responds to #[]?

ainar-g commented 7 years ago

Judging by lib/rdl/types/lexer.rex, method name can only be a word. You can directly get the types and construct it like this:

require "rdl"
require "types/core"

class A
  extend RDL::Annotate

  st = RDL::Globals.types[:string]
  at = RDL::Globals.types[:top]
  mt = RDL::Type::MethodType.new([st], nil, at)
  stt = RDL::Type::StructuralType.new({ :[] => mt })
  smt = RDL::Type::MethodType.new([stt], nil, at)

  type smt # ([[]: (String) -> %any]) -> %any
  def b(arg)
    arg["str"]
  end
end

Except that your code below the class won't typecheck:

Method type:
        ([ []: (String) -> %any ]) -> %any
Actual argument type:
        (Hash) 
Actual argument values (one per line):
        {"str"=>1}

I'd recommend using something like this until the gem's maintainers won't fix this:

  type "(Hash<String, %any>) -> %any"
  def b(arg)
    arg["str"]
  end
rmosolgo commented 7 years ago

:+1: Thanks for the details, and for the suggested work-around!

For now, I used

Hash<String, %any> or ActionController::Parameters
jeffrey-s-foster commented 7 years ago

Thanks for the bug report, this should be fixed in the dev branch now.

rmosolgo commented 7 years ago

Thanks, I'll give it a try soon!