Comment by omarqureshi

14 hours ago

I don't programme much any more but the whole beauty of Ruby that it pretty much heavily relies on #respond_to? / duck typing and thus you don't rely on types or class checking at all.

Most ruby code isn't written like that - it's written like most static languages, where objects conform to interfaces / traits / type classes / pick your poison. The community is shifting towards explicitly specifying and statically checking types now. rbs and sorbet are a testament to this.

I wouldn't say it's really a beauty of the language, it may have been the original design intent but time has shown what's actually maintainable.

  • I don’t think the existence of a library to do something is evidence of the community shifting. For me the complete absence of types from any Ruby I see IRL or in examples from conference talks, readmes etc is evidence that the community is uninterested despite tons of effort from big players.

Using #respond_to? is normally a code smell. The point of duck typing is exactly that it allows you to avoid checking the type of a class. As long as the object responds to the correct messages, the type does not matter.

  • #respond_to? is fine, it's really more #is_a? that is a code smell, in my opinion. As long as you're dispatching based on #respond_to? (i.e. "does this respond to each") by calling the method you're checking, when it does respond, you're fine as far as duck typing goes. It's when you check #is_a? and then dispatch based on type where things get weird.

    An example I always used to use was something like a method that could take a single item or a collection:

        def unpicky(something)
          if something.respond_to?(:each)
            # unpack using each or recurse to something.each do |item| unpicky(item) end
          else
            # main body
          end
        end

Structural typing like this is completely compatible with the concept of duck typing. Indeed, it's basically doing static checking of duck typing.

I don't think you're really losing the ability to check if an object responds to a message ie a_car.respond_to?(:color) just because theres type annotations. And I assume the type checker doesnt yell if you do a_car.color after that -- or if it does there's surely an equivalent to Typescript's `any` to accomplish it.

And T-Ruby apparently provides interfaces to avoid needing to do this at all (assuming both sides are written in T-Ruby I assume) https://type-ruby.github.io/docs/learn/interfaces/defining-i...

...which is awesome!

As for authoring classes, respond_to_missing?/method_missing should be rare, usually in a situation where its the only way to accomplish something. There's never been a reason to write something like:

    class Car
        def respond_to_missing?(name, priv)
            [:color, :color=].include?(name)
        end
        def method_missing(name, *args, &block)
            if name == :color
                @color
            elsif name == :color=
                @color = args.first
            end
        end
    end

Instead of

    class Car
        def color; @color; end
        def color=(value); @color = value; end
    end

Or, more idiomatically

    class Car
        attr_accessor :color
    end

And for that last case, T-Ruby apparently covers it with:

    class Car
        attr_accessor :color: String
    end