Live data from Hacker News

"DCI" in Ruby is completely broken

tonyarcieri.com

41–50 of 67 posts

Re: "DCI" in Ruby is completely broken

#41
post #29

You know, I've been thinking about this kind of thing lately. All of this mixin, traits, monkey patching hoopla is completely nonexistent in functional programming languages. To add a new method to an object you just..write a function that takes that piece of data. Imagine a language where a call looked like this: user.doSomething(1, 2, 3) But it's just syntax sugar for this function application: doSomething(user, 1,…

That's exactly how perl5 objects work:

  package Foo {
    sub new { 
      my ($class, $bar) = @_; 
      bless {bar => $bar}, $class
    }

    sub bar { 
      my ($self, $x) = @_; 
      say $self->{bar} . $x
    }
  }

  my $foo = Foo->new( 'Hello...' );

  # method call
  $foo->bar( 'baz' );       # => Hello...baz

  # function call
  Foo::bar( $foo, 'baz' );  # => Hello...baz

Re: "DCI" in Ruby is completely broken

#42
post #29

You know, I've been thinking about this kind of thing lately. All of this mixin, traits, monkey patching hoopla is completely nonexistent in functional programming languages. To add a new method to an object you just..write a function that takes that piece of data. Imagine a language where a call looked like this: user.doSomething(1, 2, 3) But it's just syntax sugar for this function application: doSomething(user, 1,…

That's pretty much exactly how it's worked in Perl for a very long time. It's very useful but it can bring in some ugliness if you don't have a strong typing system.

Re: "DCI" in Ruby is completely broken

#43
post #34
post #21

Earlier quoted context omitted.

It isn't so odd in Ruby. Ruby has always been a duck typing land. If it quacks, it is a duck. There's even a method missing handler so you can respond to any method call whatsoever. People can and do add methods directly to even built-in classes as well, the equivalent of modifying the class prototype in Javascript. So method calls are considered more messages in Ruby and saying a class will respond differently to a…

>It isn't so odd in Ruby. Ruby has always been a duck typing land. If it quacks, it is a duck. There's even a method missing handler so you can respond to any method call whatsoever. What's the benefit of this kind of typing, though? I'm pretty much just a C hacker these days, so I'm pretty ignorant about Ruby, but does this apply to the object or to the class? It seems simpler, especially on a conceptual level, to j…

>It seems simpler, especially on a conceptual level, to just initialize the the duck with everything it could possibly need to be a duck, rather than adding things dynamically.

Yes, it is simpler conceptually. But, in Rails especially where there is a tradition of moving all your application functionality into the "model" layer, you could end up with a single class that is thousands of lines of code long. For example, a User class, with a load of code about resetting passwords, that is only used in one relatively rare circumstance.

So instead, DCI says you should separate the password-reset stuff into a separate module and only add it in when needed. Both User and PasswordReset are simpler and easier to understand and only come together when needed.

(As I said elsewhere, I prefer to wrap a decorator around the User to achieve the same thing).

Re: "DCI" in Ruby is completely broken

#44
post #39

Earlier quoted context omitted.

What does extend give you that delegation doesn't?

At a code level - no need to write the delegation methods/macros. At the design level - flat hierarchy At an abstract level - thinking in terms of objects, not classes.

> At a code level - no need to write the delegation methods/macros.

You don't have to. They're built into SimpleDelegator. Syntactically the difference is you'd write:

    class MyThing 
instead of:

    module MyThing
And do:

    MyThing.new(obj)
instead of:

    obj.extend(myThing)
> At an abstract level - thinking in terms of objects, not classes.

It seems to me like you're trying to use a module to solve a problem which can be solved with an object. I don't think that's thinking in objects at all... it's thinking in modules.

Re: "DCI" in Ruby is completely broken

#45
post #5

Earlier quoted context omitted.

While this is true, it currently has pathological performance on all major Ruby implementations.

I just wonder if fixing this (by adding extend calls to cache) will end up making unicorn eat way too much memory.

The perl equivalent (run-time role application) normally caches the extended class by default and re-uses it. Provided you don't have a pathological number of combinations, that seems to work out pretty well.

Re: "DCI" in Ruby is completely broken

#46
post #29

You know, I've been thinking about this kind of thing lately. All of this mixin, traits, monkey patching hoopla is completely nonexistent in functional programming languages. To add a new method to an object you just..write a function that takes that piece of data. Imagine a language where a call looked like this: user.doSomething(1, 2, 3) But it's just syntax sugar for this function application: doSomething(user, 1,…

d calls this ufcs ("uniform function call syntax"): http://www.drdobbs.com/cpp/uniform-function-call-syntax/2327...

Re: "DCI" in Ruby is completely broken

#47

I was curious what Tony's graph would look like using a SimpleDelegator as he suggested. (using ruby 1.9.3p194) Calculating ------------------------------------- without dci 68507 i/100ms with dci 24409 i/100ms with delegator 46945 i/100ms ------------------------------------------------- without dci 2240202.4 (±3.2%) i/s - 11235148 in 5.020463s with dci 412445.3 (±3.7%) i/s - 2074765 in 5.037113s with delegator 1018…

the example in the op was somewhat misleading in that he was extending the object within its initialize method. i think you'd more often want to do a per-object extend dynamically at runtime to simulate something like smalltalk's #become, where the object would be modifying its capabilities and 'type' based on some external input conditions.

Re: "DCI" in Ruby is completely broken

#48
post #25
post #8

I'm not a Ruby programmer, so maybe I'm misunderstanding the article -- are we talking about changing an object's class hierarchy after it's already been instantiated and then complaining that it's the performance aspect that is pathological? That sounds like a completely insane way of writing software to me.

It depends. Is your app CPU-bound? If so, then DCI probably isn't a great idea for you. In fact, building it in Ruby may not be the best idea. But if your app is IO-bound (like most Rails apps), huge and it takes new developers weeks to get up to speed, then the gains from having a simple, modular code-base should save you developer-time (and hence salaries), which are much more expensive than CPUs. Having said that,…

This DCI stuff sounds like a quick way to make an app CPU-bound, if it wasn't already.

Re: "DCI" in Ruby is completely broken

#49
post #34
post #21

Earlier quoted context omitted.

It isn't so odd in Ruby. Ruby has always been a duck typing land. If it quacks, it is a duck. There's even a method missing handler so you can respond to any method call whatsoever. People can and do add methods directly to even built-in classes as well, the equivalent of modifying the class prototype in Javascript. So method calls are considered more messages in Ruby and saying a class will respond differently to a…

>It isn't so odd in Ruby. Ruby has always been a duck typing land. If it quacks, it is a duck. There's even a method missing handler so you can respond to any method call whatsoever. What's the benefit of this kind of typing, though? I'm pretty much just a C hacker these days, so I'm pretty ignorant about Ruby, but does this apply to the object or to the class? It seems simpler, especially on a conceptual level, to j…

>It seems simpler, especially on a conceptual level, to just initialize the the duck with everything it could possibly need to be a duck, rather than adding things dynamically.

It is simpler, on a conceptual level. On an implementation level however, it quickly becomes non-simple.

One of the fundamental modularity concepts is 'separation of concerns' - this isn't something you always need to do, but when a concern (a scoped set of functionality) grows large, it should be implemented separately, to keep the conceptual complexity of individual abstractions and implementations minimal.

If I implement all of the logic for handling conditions, importing data, extracting reports, managing permissions, serialization, and resource handling in the same object, it's a very complicated object. I have no way without getting a full mental model of the thing in my head that changing X about it won't break Y, or what parts of code depend on the structure of the results of calling Z.

There are plenty of ways to skin that cat, and different ones are more appropriate in different places. Often it's correct to extract the logic into a generalizable mixin, which in Ruby would be a module, and then include it into the class. Sometimes it's better to extract the logic and the concept it represents into another class that has a relationship with the original one. And sometimes it's better to separate the logic and code into a 'context' as DCI describes - I generally prefer Decorators for this, but the Rails community seems to lean toward using modules here also, largely on the weight of DHH's opinion (app/concerns/).

Re: "DCI" in Ruby is completely broken

#50
post #29

You know, I've been thinking about this kind of thing lately. All of this mixin, traits, monkey patching hoopla is completely nonexistent in functional programming languages. To add a new method to an object you just..write a function that takes that piece of data. Imagine a language where a call looked like this: user.doSomething(1, 2, 3) But it's just syntax sugar for this function application: doSomething(user, 1,…

Note that the two examples are only the same if all subroutines are global.

The difference is that (usually) methods are looked up where the classes are defined, but subroutines are looked up where they are called. So visibility is different in the case of method invocation.

Post reply on HN