Live data from Hacker News

The Dynamic Def – abusing Ruby's def statement

weblog.jamisbuck.org

11–20 of 87 posts

Re: The Dynamic Def – abusing Ruby's def statement

#11
post #9

Earlier quoted context omitted.

In its place, being able to redefine equality on value types really helps clarify code. Misused, it creates confusion. The problem is that it's easy to think you've got a case where it helps, when actually it's not well-defined. Usually that revolves around there being state you care about which is missed from the equality comparison. Another trap is redefining #== without also looking at #eql?, which means Hash does…

Can you give me an example of where redefining equality makes sense?

Without explicit comments/documentation it is hard to imagine a good example. Its a longstanding issue. Even Lisp has 'equals' and 'equals?' which is an abomination. One looks for identity of object; the other for identity of value (if I understand it right). These kind of things are bug factories.

Re: The Dynamic Def – abusing Ruby's def statement

#12
post #9

Earlier quoted context omitted.

In its place, being able to redefine equality on value types really helps clarify code. Misused, it creates confusion. The problem is that it's easy to think you've got a case where it helps, when actually it's not well-defined. Usually that revolves around there being state you care about which is missed from the equality comparison. Another trap is redefining #== without also looking at #eql?, which means Hash does…

Can you give me an example of where redefining equality makes sense?

Anytime you have some value type. Say a `Money` class for instance.

The default `==` from Object compare identity, so unless you define it `Money.new(20, 'USD') != Money.new(20, 'USD')`.

Re: The Dynamic Def – abusing Ruby's def statement

#13

Ruby is really fun to program in, but debugging it can be hell. I would not be very enthusiastic about debugging this code. But still, this is a really cool trick that I didn't know you could pull off with Ruby, so thanks for sharing!

Agree about debugging. To my thinking, though, there are two types of "programming": professional (for day jobs, where debugging, testing, and maintainability matter), and recreational (where the point is to just explore new things and try crazy stuff that no one in their right mind would ever "really" do). This "def" stuff falls into the latter category.

Honestly, I wish there were more people doing posts about recreational programming topics. WhyTheLuckyStiff was one of the last great recreational Rubyists. I miss that kind of no-holds-barred exploration.

Re: The Dynamic Def – abusing Ruby's def statement

#14
I've somehow always found Ruby to be opposite to the Unix philosophy of doing one thing and doing it well. While Ruby may seem trivial and fun in the beginning, it tends to be cumbersome and maintainable as the size of the repository grows. Coming from a Python world, my first reaction to Ruby was that it was more like Perl where there are many ways to achieve the same thing, and no it was not really helpful if you inherited poorly written code.

Re: The Dynamic Def – abusing Ruby's def statement

#15
post #10
post #6

Earlier quoted context omitted.

> The oauth gem, for instance, redefines '==(val)' on the AccessToken to 'Base64.encode(self.signature) == Base64.encode(val).' Is this just a complaint about the ability to overload operators in general?

It's a complaint about the ability to redefine many things that shouldn't be redefine-able, operators included.

Very right. Operator overloading is a self-indulgent trick. The only one to benefit is the author. Subsequent readers are mostly confused.

Instead of overloading, I've always wanted to define new operators e.g. used as 'int x = v1 v2;'

If you're going to do something with operators, at least let me make descriptive ones.

Re: The Dynamic Def – abusing Ruby's def statement

#16
post #12
post #9

Earlier quoted context omitted.

Can you give me an example of where redefining equality makes sense?

Anytime you have some value type. Say a `Money` class for instance. The default `==` from Object compare identity, so unless you define it `Money.new(20, 'USD') != Money.new(20, 'USD')`.

Right, this seems like exactly why we shouldn't be allowed to redefine it. As a reader of your code that redefines ==, I think == means we are talking identity until I find your function that redefines ==. It has made it so I need to understand more things in order to be able to reason about your code. That seems like a negative to me.

Re: The Dynamic Def – abusing Ruby's def statement

#17
post #10

Earlier quoted context omitted.

It's a complaint about the ability to redefine many things that shouldn't be redefine-able, operators included.

Very right. Operator overloading is a self-indulgent trick. The only one to benefit is the author. Subsequent readers are mostly confused. Instead of overloading, I've always wanted to define new operators e.g. used as 'int x = v1 v2;' If you're going to do something with operators, at least let me make descriptive ones.

> Operator overloading is a self-indulgent trick. The only one to benefit is the author.

That's a great way to put it.

Re: The Dynamic Def – abusing Ruby's def statement

#18
post #16
post #12

Earlier quoted context omitted.

Anytime you have some value type. Say a `Money` class for instance. The default `==` from Object compare identity, so unless you define it `Money.new(20, 'USD') != Money.new(20, 'USD')`.

Right, this seems like exactly why we shouldn't be allowed to redefine it. As a reader of your code that redefines ==, I think == means we are talking identity until I find your function that redefines ==. It has made it so I need to understand more things in order to be able to reason about your code. That seems like a negative to me.

That's because you are among the people who don't like abstractions. I disagree with that opinion, but that's fine, Ruby is just definitely not for you.

Don't try to change or complain about Ruby, you are likely more happy with languages like go.

Re: The Dynamic Def – abusing Ruby's def statement

#19
post #4

Cool article about the craziness of Ruby. Ruby is a frustrating language. The oauth gem, for instance, redefines '==(val)' on the AccessToken to 'Base64.encode(self.signature) == Base64.encode(val).' This stuff feels really dangerous and unnecessary. I spend a lot of time on code reviews pointing out bad features of Ruby (and Rails) that we shouldn't be using because they break application flow and make it significan…

Ruby is a wonderful (and wonderfully powerful) language. Unfortunately, some of the popular ruby libraries (gems) are... problematic. To put it nicely. The web/rails gems in particular can be nasty minefields.

However, the language itself is great. The trick is to remember the usual advice that just because you can doesn't mean you should. Too many gems add "clever" metaprogramming (such as the def tricks in this article), when it isn't actually making the program simpler.

The beauty of ruby is that while it can act almost like a LISP for the times when you want powerful metaprogramming features, while also allowing simple shell or C style imperative code when that is more appropriate.

(Of course, some people fear that type of freedom... https://vimeo.com/17420638 )

Re: The Dynamic Def – abusing Ruby's def statement

#20
Zork-alikes aside... I'm looking for practical applications :)

I recently re-watched Yaron Minsky's "Effective ML" talk where (towards the end) he talks about making read-only and read-write types. That's where I thought Jamis was going with the state machine example: one could tell an object "yo, make yourself immutable!" (ie redefine your methods so that you can't change yourself). But in an OO world that seems more neatly achieved with subclassing. [To the extent of faking anything "immutable" in ruby"].

There's #freeze I suppose... and no #unfreeze. Which is perhaps sensible :) And #freeze only guards against new assignment to instance variables; it doesn't guard against an instance method mutating the content of an instance variable (def esquirify! ; @name Python has this "inner-methods" capability (with saner scoping) which is a great antidote for python's limited lambdas. But that's not a problem with ruby.

It's a neat trick (and nice to read something from Jamis again). Are there any sensible use cases?

Post reply on HN