hoodwink.d enhanced
RSS
2.0
XHTML
1.0

RedHanded

Mixins, But With Tablespoons #

by why in inspect

Mauricio’s at it again. I solemnly swear that I will be linking to him greater than five times over this next month and each time you will be fine with it. See, watch how fine you’ll feel when you read about his remake of Module#include.

The include method is basically the mixin method in Ruby. You have a class that has its own each method. You mixin the Enumerable module and you get all these neat methods for free like inject and select and sort_by and on and on.

 class MyList
   include Enumerable
   def each; ... end
 end

What if you don’t want the sort method, though? You want to deposit only granular measurements of a mixin. This is what Mauricio tackles. And his new include goes like this:

 class MyList
   include Enumerable,
     :exclude => :sort_by,
     :alias => {:detect, :siphon}
 end

All part of a LazyWeb query from Dan. And Mauricio’s code is only 17 lines long, very easy to peruse.

said on

I like it, but I’ll make the same comment I was going to make on Dan’s journal (didn’t because Dan has comments off for non LJers):

I’d prefer a syntax that looks like this:


class MyList
  include Enumerable,
    :detect => :siphon,
    :sort_by => nil
end

In other words, suck the tail of the argument list into a hash. For each method that would be included, look in that hash. If the key is there but with a value of nil, exclude that method. If the key is there with a non-nil value, include it under the alias.

To me, such a syntax would be much cleaner and unified…

said on

And here’s the implementation for the alternate syntax proposed above (shamelessly tweaked from Mauricio’s code, not tested):


class Class
  old_include = instance_method(:include)
  define_method(:include) do |*args|
    aliases = (Hash === args.last) ? args.pop : {}
    m = Module.new
    args.each{ |mod| m.module_eval{ include mod } }
    aliases.each_pair do |old, new|
      m.module_eval do
        alias_method(new, old) if new
        undef_method(old)
      end
    end
    old_include.bind(self).call(m)
  end
end

said on

This notation isn’t so good either for Nitro and Og which uses dynamic modules. Hash parameters on #include can alter the behavior of the module via these include options.

Like I said on Mauricio site, Facets goes about it in slightly more Rubyeque way—a block is used with a mini-DSL to alter the new module. I named the method #integrate, instead of resusing #include, but it could be applied to #include just as well.

said on

Maybe I should give and example here too:

class MyList
  itegrate Enumerable do
    remove :sort_by
    rename :detect, :siphon
  end
end

The internal part of the block is nothin special, its just be class_evaled on the new module—I’ve just created a few new useful method for this context. You could even define a new “one-time” method in it.

said on

wow, that’s quite some pressure you’re putting on me, _why… 5+ worthy blog entries is a lot :)

said on

Hm, I kinda like both lukfugl’s and trans’ ideas.

/me ponders

said on

Haskell’s type classes are a really snazzy way of doing this… The syntax could be emulated by ruby syntax trivially I imagine.

said on

But can this be used to implement perl6-style roles? Because that would be a neat thing to have in ruby.

said on

Definitely. Both permutations above are excellent! Very well shown.

said on

Tran’s permutation is great (an added plus: it integrates nicely with the dynamic mixins used in Nitro/Og ;-))

said on

Can I just say, I don’t like the syntax the alias parameter is given in. {:detect, :siphon} prefers to be {:detect => :siphon}. 1) It looks less similar to alias detect siphon (which would imply the reverse alias) and 2) it is less easily confused with an array or block or anything other than the hash it is.

said on

crzwdjk, if you happen to read my journal, my original idea was inspired by Curtis Poe’s Class::Trait Perl module.

While this solution wouldn’t match the trait spec completely (i.e. the Schaerli paper), it does deal with composition, which is the only part of traits I think we need for Ruby.

Also, see David Naseby’s blog entry (from a while back) on a theoretical implementation of traits here. It was that blog entry that I based the “interface” package on.

said on

Can I ask for matz’ proposed syntax?

E= Readable+Writable+Enumerable - [:sort_by] 
E.rename {:detect=>siphon}
include E

I admit I love it when I see math operator overriding.

said on

Traits Algebra? Hmm… then why not:

E=Readable+Writable+Enumerable-[:sort_by]-{:detect=>:siphon}

(Note: If the second plus didn’t show it’s _why’s fault!)

Still, that might be a little too terse, though it certainly seems very cool on first sight.

said on

I love trans’ (non-algebraic) iteration

+1

said on

riffraff, ick.

said on

Hmm, the thing I’d like more than anything else would be the ability to only include those bits which I explicitly request.

I mean, okay, so you can explicitly something which would conflict today—but what if the module adds a conflicting method in the future?

That’s one thing that’s bothered me about Ruby for a long time. When everything’s the equivalent of a Java-ish star-import (e.g. import com.blah.*), you live dangerous-like.

said on

So maybe

MappableAndFindable = Enumerable & [:map, :find]

But that is going to break things if the module’s methods depend on now-non-existing methods.

said on

Module arithmetics implementation (was going to post here but it got a bit long)

said on
Qux = (Foo & [:map, :read]) - { :map => :krunk }
Do people think that actually looks good? I think it looks ugly.
said on

It’s like array set operation notation. Is the following more readable?

Qux = Foo.merge(Bar).
      only(:map, :read).
      rename(:map => :krunk)

Comments are closed for this entry.