public
Last active

Partial application on rubinius. Apply the diff to rubinius project and run `rake build`. Idea by @josevalim, code by @wycats.

  • Download Gist
rbx_partial_application.diff
Diff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
diff --git a/lib/compiler/ast/transforms.rb b/lib/compiler/ast/transforms.rb
index 19e1cfb..4e52ec2 100644
--- a/lib/compiler/ast/transforms.rb
+++ b/lib/compiler/ast/transforms.rb
@@ -59,6 +59,71 @@ module Rubinius
end
end
+ # Provide an underscore node that allows partial application in Ruby.
+ # Instead of doing a method call, we are going to generate a lambda
+ # that allow us to pass argument where undercores were placed.
+ #
+ # Conceptual examples:
+ #
+ # puts(_)
+ # #=> lambda { |x| puts(x) }
+ #
+ # self.puts(_)
+ # #=> lambda { |x| self.puts(x) }
+ #
+ # object.puts(_)
+ # #=> lambda { |x| object.puts(x) }
+ #
+ # puts(_, :a, _)
+ # #=> lambda { |x,y| self.puts(x, :a, y) }
+ #
+ # Practical examples:
+ #
+ # [1,2,3].each &puts(_)
+ # #=> 1\n2\n3\n
+ #
+ # { :send => true, :to_s => false }.select &object.respond_to?(_, _)
+ # #=> [:send, :to_s]
+ #
+ class Underscore < SendWithArguments
+ transform :default, :underscore, "Partial application using _"
+
+ def self.match?(line, receiver, name, arguments, privately)
+ return nil unless arguments.is_a?(ArrayLiteral)
+
+ if arguments.body.size > 0 && arguments.body.any? { |arg| arg.is_a?(Send) && arg.name == :_ }
+ ret = new line, receiver, name, arguments, privately
+ end
+ end
+
+ def bytecode(g)
+ block_args = []
+ arg = 0
+
+ send_args = []
+
+ arguments.array.each do |argument|
+ if argument.instance_of?(Send) && argument.name == :_
+ block_args << LocalVariableAssignment.new(line, :"_partial#{arg}_", nil)
+ send_args << LocalVariableAccess.new(line, :"_partial#{arg}_")
+ arg += 1
+ else
+ send_args << argument
+ end
+ end
+
+ send_args = ArrayLiteral.new(line, send_args)
+
+ masgn = MultipleAssignment.new(line, ArrayLiteral.new(line, block_args), nil, nil)
+ masgn.iter_arguments
+
+ send_node = SendWithArguments.new(line, receiver, name, send_args, privately)
+ iter = Iter.new(line, masgn, send_node)
+
+ iter.bytecode(g)
+ end
+ end
+
##
# Handles Rubinius.primitive
class SendPrimitive < SendWithArguments

AKA "cut" in Haskell? Pretty awesome.

Sean, what is Haskell's 'cut'?

@akahn It's a pattern for partial application that uses the underscore (sorry, I was mistaken, it came from Scheme). There's a beautiful port of the idea to Erlang (which also includes monads) called erlando (video/slides).

@seancribbs cool, I didn't know Erlang could do that stuff.

@akahn Yeah, it has functions called "parse transforms" that can be run on the AST to transform it before it reaches the bytecode transformation step.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.