hoodwink.d enhanced
RSS
2.0
XHTML
1.0

RedHanded

Choice Python Hack: Infix Operators #

by why in bits

Pencils down. Cool hack for Python right here. I guess this was up on LtU a few days ago. I saw it on The Farm, so.

Works like:

 # simple multiplication
 x=Infix(lambda x,y: x*y)
 print 2 |x| 4
 # => 8

 # class checking
 isa=Infix(lambda x,y: x.__class__==y.__class__)
 print [1,2,3] |isa| []
 print [1,2,3] <<isa>> []
 # => True

 # inclusion checking
 is_in=Infix(lambda x,y: y.has_key(x))
 print 1 |is_in| {1:'one'}
 print 1 <<is_in>> {1:'one'}
 # => True

Anyway, this hacker—Ferdinand Jamitzky—sure would fit in well over here. He’s tread so close to our territory. What with the Ruby-like syntatic sugar he supplied for Python last year.

 5 *times(lambda: printf("Hello"))
 [1,2,3,4] *each(lambda x: printf("Count:"+str(x)))
 print [1,2,3,4,5] *length
 ['a','b','c','d','e'] *each(lambda char: char+'!')

I mean if he can make Python look like Ruby, maybe he can make Ruby look like dolphin sonar. Can you best these??

said on 22 Feb 2005 at 17:08

Here’s my Ruby implementation. We seem to be reading the same stuff lately.

Note that Ruby even lets you define Unicode operators like 5 |∈| [1, 2, 3].

require 'thread'

def infix(name, code = nil, &block)
  if code and block or
     code.nil? and block.nil?
  then
    raise(ArgumentError, "Have to specify code or block")
  end

  block ||= code

  Kernel.send(:define_method, name) do
    Infix.new(name, block)
  end
end

class Infix
  class LCall
    def initialize(name, op, type, left)
      @name, @op, @type, @left = name, op, type, left

      case @type
        when "|" then
          def self.|(right)
            @op.call(@left, right)
          end

        when "<<" then
        def self.>>(right)
            @op.call(@left, right)
          end 
   end
    end
  end

  def initialize(name, op)
    @name, @op = name, op
  end

  def lcall(type, left)
    LCall.new(@name, @op, type, left)
  end
end

ops = {
  '|' => 'old_or_op',
  '<<' => 'old_lsh_op'
}

overload_op = lambda do |op, mod|
  mod.module_eval do
    if instance_methods(false).include?(op) then
      old_op = instance_method(op)
      undef_method(op)
    end

    define_method(op) do |other|
      if other.class == Infix then
        other.lcall(op, self)
     elsif old_op then
        old_op.bind(self).call(other)
      else
        msg = "undefined method `#{op}' for #{self.inspect}:#{self.class}" 
        raise(NoMethodError, msg)
      end
    end
  end
end

init = true

overload_ops = lambda do |mod, singleton|
  ops.each do |op, old_name|
    overload_op.call(op, mod)
  end

  unless singleton
    mod.module_eval do
      define_method(:singleton_method_added) do |name|
     return if @overload_op

        Thread.exclusive do
          if not init and ops.include?(name.to_s) and
             self.class != Infix::LCall
          then
            @overload_op = true
            overload_op.call(name.to_s, class << self; self; end)
            @overload_op = false
          end
        end 
      end         
    end

    class << mod; self; end.module_eval do
      define_method(:method_added) do |name|
        return if @overload_op

        Thread.exclusive do
          if not init and ops.include?(name.to_s) and
            self != Infix::LCall
          then
  @overload_op = true
            overload_op.call(name.to_s, self)
            @overload_op = false
          end
        end
      end
    end
  end
end

overload_obj_ops = lambda do |obj|
  if not obj.methods(false).empty? then
    singleton = class << obj; self; end
  overload_ops.call(singleton, true)
  end 
end

ObjectSpace.each_object(Module) do |mod|
  overload_ops.call(mod, false) if mod != Infix::LCall
end

ObjectSpace.each_object(Object) do |obj|
 overload_obj_ops.call(obj) if obj.class != Infix::LCall
end

init = false

infix :in? do |left, right|
  right.include? left
end
said on 22 Feb 2005 at 17:33

Well, my solution isn’t nearly as robust as flgr, but it seems to do the job:

class Infix
  def initialize( &block )
    @op = block
  end
end

def +(a)
  if @op.arity == 1
    @op[a]
  else
    Infix.new { |b| @op.call(a,b) }
  end
end

def <(a)
  Infix.new { |b| @op.call(a,b) }
end

def >(a)
  @op.call(a)
end

def coerce(other)
  [self,other]
end

In = Infix.new { |left,right| right.include? left }

puts( 5 <In> [ 1, 2, 3, 4, 5 ] )
puts( 5 +In+ [ 6, 7, 8, 9, 0 ] )
said on 23 Feb 2005 at 01:25

why: code in comments is all icky. Jamis’s code is partly behind this comment box and continues well below. And flgr’s code seems to be hiding behind Jamis’s code. This is Firefox 1.0 on OSX , which ought to be the same as Firefox 1.0 on anything.

said on 23 Feb 2005 at 02:43

Ok. Does anyone care to explain flgr’s solution to people with less developed cortices?

said on 23 Feb 2005 at 03:14

Right. In the old tradition of answering ones own question, here I go.

Basiccally, flrg’s solution overrides ’|’ and ‘<<’ to call an instance of ‘Infix::LCall’. This instance has overridden either ’|’ or ‘>>’.

This allows you to write, for example

5 |in| [1,3,5]

The interpreter sees this (more or less as)

(Kernel.|(5)).|([1,3,5])

where the last ’|’ is the one overridden by Infix::LCall, as you can see from:


puts(5 | in?)
#=> #Infix::LCall:0x40139fa0

Beautiful. Yet at the same time I smell just a little hint of utter perversion.

My compliments!

said on 23 Feb 2005 at 08:03

Note that most of the work my solution does is overriding the x.| in x |infix| y by usings ObjectSpace, method_added and lots of other magic. The python guys don’t need to do that as they already seem to have right-side operators for exactly that. But then again they can not define Unicode operators in this way.

said on 23 Feb 2005 at 15:54

So other than sugar, when would someone use this?

And yea, _why contributed scrollbars unto the code tag, and there was much rejoicing as the resize keys in firefox where tired from their repeated striking. Ok, is it _why or why ?

said on 24 Feb 2005 at 10:48

_why do you ask?

said on 24 Feb 2005 at 11:08

the underscore clears up ambiguity. gheesh, tell me about it.

Comments are closed for this entry.