Arc Forumnew | comments | leaders | submitlogin
Refinements in Ruby: better support for monkeypatching (headius.com)
2 points by akkartik 4684 days ago | 5 comments


2 points by rocketnia 4683 days ago | link

I wonder if Ruby could use something like ECMAScript 6's private names instead.[1] The method name itself would be looked up from the lexical scope, neatly letting the lexical context play a part in resolving a dynamically dispatched method call.

I could be missing something big here. I don't really know all the required use cases, having hardly used Ruby myself.

[1] http://wiki.ecmascript.org/doku.php?id=strawman:private_name...

-----

1 point by Pauan 4683 days ago | link

"It perplexed me that you didn't take that point of view in the first place. :) From that blog post, Ruby's refinements strike me as a snowball of complexity arising from the existing use of monkey patching in the wild. Nulan has no entrenched user base and no monkey patching mechanism (that I can see offhand), let alone a culture of monkey patching, so you have little to worry about. :-p"

Because I was still mulling over the particular problems that that blog post mentioned, and trying to figure out if they applied to Nulan or not. And yes, it does strike me as overly complex, but then again, Ruby already has classes + mixins + 2 or more forms of eval + instance-specific anonymous classes + like 5 different kinds of blocks...

---

"Actually, since Nulan's an fexpr language, its programmers potentially have to second-guess how every single expression will be evaluated. XD Yes you may have idioms to manage this paranoia, but that doesn't make Nulan exempt to the blog post's point, since Ruby could develop idioms for this too."

I'm actually changing Nulan to use macros, for the sake of speed and to make it easy to compile to JS. But yes, fexprs do cause that same sort of second-guessing. And as both you and I pointed out, Nulan is vulnerable, but the idioms are different. In particular, hyper-static scope helps a lot I think.

---

"I wonder if Ruby could use something like ECMAScript 6's private names instead.[1] The method name itself would be looked up from the lexical scope, neatly letting the lexical context play a part in resolving a dynamically dispatched method call."

Well, that's pretty much exactly Nulan's "unique types" system. Not that I'm saying it'd be a bad thing for Ruby to implement that. And I'm actually quite pleased that that system is on the table for JS. I really do feel it's the most flexible type system while still remaining hygienic.

-----

1 point by Pauan 4683 days ago | link

Cool. I wondered when Ruby would get something like that.

Thankfully, I believe Nulan avoids most of that issue by A) using global names, so you could simply define a new "camelize" function, no need to attach it to the String class, and B) a combination between mutable variables/hyper static scope/unique types/Arc-style "extend" to control extensibility.

I'd be interested in seeing some examples where Ruby's refinements are better than Nulan's system.

---

Some interesting points:

"Because there's no "using" anywhere in the code, and we're not extending some other class, most folks will assume we're simply concatenating strings here. After all, why would I expect my "+" call to do something else? Why should my "+" call ever do something else here?"

"The "module_eval" behavior of refinements simply goes too far. It forces every Ruby programmer to second-guess every block of code they pass into someone else's library or someone else's method call. The guarantees of standard method dispatch no longer apply; you need to know if the method you're calling will change what calls your code makes. You need to understand the internal details of the target method. That's a terrible, terrible thing to do to Rubyists."

I'm fairly sure Nulan avoids both these issues, but I think this kind of "magical action at a distance" is consistent with Ruby's hyper-liberal viewpoint.

-----

1 point by Pauan 4683 days ago | link

Hm, thinking about it some more... Nulan does technically suffer from the same problems as Ruby, because of mutable variables. I took this example (https://gist.github.com/4110634#file_ref_8.rb) and tried rewriting it in Nulan style:

  # library1
  (types %inject)

  (def (add-all [ %inject f ])
    (f "" -> str acc (+ acc str)))

  # application
  (import library1)

  (def (my-+ x y) "@x plus @y")

  (def (my-array)
    [ %inject -> str f
                (do (each {"foo" "bar" "qux"} -> x
                      (set! str (let! + my-+ (f x str))))
                    str) ])
In this case, we're using "let!" to dynamically rebind "+" to "my-+". I think this is okay, though, for two reasons:

1) It's not idiomatic to assign to variables. Instead, it's idiomatic to use unique types to create extensible functionality. Mutability is discouraged in general (though allowed).

2) It's also possible to protect against such things, if you really want to. For instance, you could do this:

  # library1
  (types %inject)

  (def (add-all [ %inject f ])
    (f "" -> str acc (+ acc str)))

  (var + +)
This is clunky, though, having to manually make everything safe to use. If I really wanted to, I could make a macro that automatically protects against such things:

  # library1
  (protect-all
    (types %inject)

    (def (add-all [ %inject f ])
      (f "" -> str acc (+ acc str))))
But I, personally, don't see much reason for that since as point 1 said, it's not idiomatic to assign to variables.

Of course, I guess the same argument could be used for Ruby... if refinements aren't idiomatic, then it's not a big deal, right? Well, except that mutable monkey-patching actually is idiomatic in Ruby, refinements are just a way to scope them.

In any case, I feel that due to Nulan's different idioms and such, the kinds of problems Ruby has generally aren't an issue.

-----

2 points by rocketnia 4683 days ago | link

"In any case, I feel that due to Nulan's different idioms and such, the kinds of problems Ruby has generally aren't an issue."

It perplexed me that you didn't take that point of view in the first place. :) From that blog post, Ruby's refinements strike me as a snowball of complexity arising from the existing use of monkey patching in the wild. Nulan has no entrenched user base and no monkey patching mechanism (that I can see offhand), let alone a culture of monkey patching, so you have little to worry about. :-p

---

"It's not idiomatic to assign to variables [in Nulan]."

Well, I wonder what Ruby idioms were like before Rails came along! EDSLs often use every readability-enhancing language feature they can get their paws on, regardless of the intended stability of those features.

I'm a culprit of this. Consider my Arc module system, where namespaces work by assigning macros to the global scope, calling 'eval on a module body, and removing the macros again. When my module system didn't work in Jarc and Rainbow, I considered Jarc and Rainbow to be nonconformant, even after jazzdev and conanite fixed most of the obvious bugs and probably conformed to pg's intended design about as well as pg-Arc did. :)

---

Blog post: "It forces every Ruby programmer to second-guess every block of code they pass into someone else's library or someone else's method call."

You: "I'm fairly sure Nulan avoids [this issue]"

Actually, since Nulan's an fexpr language, its programmers potentially have to second-guess how every single expression will be evaluated. XD Yes you may have idioms to manage this paranoia, but that doesn't make Nulan exempt to the blog post's point, since Ruby could develop idioms for this too.

---

"[...]but I think this kind of "magical action at a distance" is consistent with Ruby's hyper-liberal viewpoint."

Yeah, when the blog post talks about a very dynamic language and warns about how certain features might threaten to break its local static invariants, there's a sense of whiplash there. :-p But it does make sense to care about local static reasoning in this case, since that's (at least informally) what refinements are supposed to bring to monkey patching in the first place.

-----