Skip to content

Instantly share code, notes, and snippets.

@josevalim
Created September 17, 2011 20:59
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save josevalim/1224361 to your computer and use it in GitHub Desktop.
Save josevalim/1224361 to your computer and use it in GitHub Desktop.
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
Copy link

AKA "cut" in Haskell? Pretty awesome.

@akahn
Copy link

akahn commented Sep 18, 2011

Sean, what is Haskell's 'cut'?

@seancribbs
Copy link

@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
Copy link

akahn commented Sep 19, 2011

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

@seancribbs
Copy link

@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