Choice Python Hack: Infix Operators #
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??


flgr
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 endJamis
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 ] )slumos
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.
kHebSkik
Ok. Does anyone care to explain flgr’s solution to people with less developed cortices?
kHebSkik
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
The interpreter sees this (more or less as)
where the last ’|’ is the one overridden by Infix::LCall, as you can see from:
Beautiful. Yet at the same time I smell just a little hint of utter perversion.
My compliments!
flgr
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.
norseman
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 ?
muir
_why do you ask?
why
the underscore clears up ambiguity. gheesh, tell me about it.
Comments are closed for this entry.