Create a gist now

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Partial application on rubinius. Apply the diff to rubinius project and run `rake build`. Idea by @josevalim, code by @wycats.
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
@seancribbs

This comment has been minimized.

Show comment
Hide comment
@seancribbs

seancribbs Sep 17, 2011

AKA "cut" in Haskell? Pretty awesome.

AKA "cut" in Haskell? Pretty awesome.

@akahn

This comment has been minimized.

Show comment
Hide comment
@akahn

akahn Sep 18, 2011

Sean, what is Haskell's 'cut'?

akahn commented Sep 18, 2011

Sean, what is Haskell's 'cut'?

@seancribbs

This comment has been minimized.

Show comment
Hide comment
@seancribbs

seancribbs Sep 18, 2011

@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).

@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).

@akahn

This comment has been minimized.

Show comment
Hide comment
@akahn

akahn Sep 19, 2011

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

akahn commented Sep 19, 2011

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

@seancribbs

This comment has been minimized.

Show comment
Hide comment
@seancribbs

seancribbs Sep 19, 2011

@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.

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment