Beginner in ruby world, I would like to do something like:
[1,2.0,"a",2].select(&:is_a?(Integer))
but like this it definitely don't work...
Any ideas?
You can't do what you are asking for because when you use the & syntax you have to use a method that doesn't take parameters.
However, if you for some reason you really want to do something like that, you need to make a method that doesn't take parameters like so:
class Object
def is_an_integer?
is_a? Integer
end
end
You can then do:
[1,2.0,"a",2].select(&:is_an_integer)
&:method_name is syntactic sugar for &:method.to_proc. Enumerators like select and whatnot accept a block and yield each element of the enumerator to the passed block. That is:
[1,2,3].select &:even?
is equivalent to:
p = :even.to_proc
[1,2,3].select {|val| p.yield(val) }
Since only the parameters yielded by the enumerator are yielded to the proc, you would have to include them in the source list. That is, we might expect:
[[1, Integer]].select &:is_a?
to result in:
select {|*args|, p.yield(*args) }
However, remember that p isn't a method bound to any particular class! It's going to try to invoke the given method on the passed argument. So, it's going to try to invoke Array#is_a? with no arguments, rather than splatting the arguments out and invoking Integer#is_a?(Integer).
So, to accomplish this, we'll have to somehow create a proc that binds the passed arguments, and then calls the given method on the yielded receiver with the passed args. We can do this by adding a method to the Symbol class:
class Symbol
def with_args(*args)
proc {|receiver| receiver.send(self, *args) }
end
end
[1, "a"].select &:is_a?.with_args(Integer)
While it's perhaps not amazingly clean, it does work.
Related
This article touches on the issues but doesn't give a solution.
This started when I wanted to write a method and optionally pass it an argument which could be null or a ???? (proc, lambda, method, block, ???). Lets call it, for now, a block because a block works. The block takes one required argument. An example of the method and a call to it would be:
#!/usr/bin/env ruby
def foo(&proc)
puts "before"
if proc
yield "passed to proc"
end
puts "after"
end
def add_message(s)
puts "from add_message #{s}"
end
foo { |s| add_message(s) }
foo
And the output is:
before
from add_message passed to proc
after
before
after
Great. But, what I'd like to do is be able to call foo like this: foo(&:add_message). But I can't. Changing line 15 above I get:
before
./temp.rb:11:in `add_message': wrong number of arguments (given 0, expected 1) (ArgumentError)
from ./temp.rb:6:in `foo'
from ./temp.rb:15:in `<main>'
And, as the article above mentions, the arity is now -2. So, how do I write a simple method like add_message that I can use with &:add_message. OR!!! as is the case 99.99% of the time, please set me on the proper track on how to do this.
The problem is that Symbol#to_proc does not create a proc that calls add_message method correctly.
# `yield` will pass its arguments to proc
>> :add_message.to_proc.call('passed to proc')
# => ArgumentError
This calls 'passed to proc'.add_message, because our method is defined in Object it works when called on String, however it is missing the required argument.
The solution is to make a proc that can accept the same arguments as add_message method and pass them along to that method. We can use Object#method that returns Method object that implements its own to_proc and has the same arity as the method.
>> method(:add_message).to_proc.arity
=> 1
>> method(:add_message).to_proc.call('passed to proc')
from add_message passed to proc
>> foo(&method(:add_message))
before
from add_message passed to proc
after
From the Ruby docs
Conversion of other objects to procs
Any object that implements the to_proc method can be converted into a proc by the & operator, and therefore can be consumed by iterators.
class Greeter
def initialize(greeting)
#greeting = greeting
end
def to_proc
proc {|name| "#{#greeting}, #{name}!" }
end
end
hi = Greeter.new("Hi")
hey = Greeter.new("Hey")
["Bob", "Jane"].map(&hi) #=> ["Hi, Bob!", "Hi, Jane!"]
["Bob", "Jane"].map(&hey) #=> ["Hey, Bob!", "Hey, Jane!"]
Of the Ruby core classes, this method is implemented by Symbol, Method, and Hash.
So when you pass an argument with a unary ampersand before it, to_proc gets called on it. The &: "syntax" is actually & being called on a symbol literal, i.e. &(:foobar), and Symbol.to_proc has the behavior of converting a symbol into a method call on its first argument, i.e. these two are roughly equivalent (modulo named argument forwarding)
:foobar.to_proc
proc { |x, *args| x.foobar(*args) }
Ruby's Method type also implements to_proc, so if you have a standalone method called foobar (on a module, say, Example), then you can call Example.method(:foobar) and get an &-compatible object. If you have a "top-level" method, then it's probably being defined on the main object and calling method with no explicit receiver will work.
The other type mentioned in that quote is hashes, which can be turned into a function mapping their keys to their values (and returning nil if no matching key exists). And, of course, you can always implement a method called to_proc on your own classes and it'll work just as well as any built-in type.
class Integer
def set
return self + 1
end
end
p [1,2,3,4,5,6].map(&:set)
I think when you can use &: syntax that a method have been defined for a class like above
So this works(Pulled from Code-Academy):
def greeter
yield
end
phrase = Proc.new {
puts "Hello there!"
}
greeter(&phrase)
I understand what yield is doing, and understand what a Proc does. However "why" is this allowed?
IE: greeter method has no parameters defined, not even optional ones. So why does ruby allow us to pass something to it? (In this case a reference to a block of code (The Proc phrase).
I mean it's great that it does, but rule-wise this seems like it shouldn't be allowed?
&phrase is not a reference. It is the Ruby annotation for passing a block explicitly. Here , it is converting the proc to the implicit block for the method call. Since every method accepts a default block as an argument, your code works.
Here is an expectation that utilizes a custom RSpec matcher, yield_variables:
specify { expect{ |p| [3,4,5].my_each(&p) }.to yield_variables [3,4,5] }
my yield_variables matcher's matches? method utilizes a custom class called Probe (Probe is a stripped down version of RSpec's yield probe):
...
def matches? block
ap Probe.probe block
# note i am inspecting what is returned by Probe.probe with ap
end
...
# Probe class is what all my questions are about!
class Probe
attr_accessor :yielded_args
def initialize
self.yielded_args = []
end
def self.probe(block)
probe = new
block.call(probe)
probe.yielded_args
end
def to_proc
Proc.new { |*args| yielded_args << args }
end
end
Now my ap inside matches? reports this: [3,4,5] That is what I expect. However, I have no idea how the Probe class works!!
Problem 1) the matches? block
Normally, the argument we pass to matches? is what we expect the subject to return. i.e, I expect [3,4,5] to be passed into block.
Instead, |p| [3,4,5].my_each(&p) is passed into block, as a proc. Why is this?
Problem 2) block.call(probe)
I'm a bit shakey on procs so please explain slowly. But basically, here we take a new instance of my Probe class and 'send' the block to it, as an argument. That's how I'd explain it to the best of my abilities, but I might have it totally wrong so please explain slowly.
Problem 3) How is to_proc called?
How on earth is .to_proc called automatically? I believe it's called automatically by block.call(probe). Is to_proc an automatically called method like initialize? Is it automatically called whenever the class is sent to a proc? (Btw, the phrase the class is sent to a proc doesn't even make 100% sense to me - please explain. The block isn't passed into the class as an argument anymore. Or if the block is passed as an argument the block.call syntax feels really weird and backwards)
Problem 4) to_proc
How does to_proc have access to the expectation's subject i.e. |p| [3,4,5].my_each(&p) ? How is Proc.new 's *args automatically populated with every single possible yield argument, even though I've only passed in |p| ? How does Proc.new loop along with my my_each, incrementally placing all my args in an array? How does Proc.new have any knowledge of the subject |p| [3,4,5].my_each(&p)? How?? Explain please, and thanks.
Block based marchers work a little differently to other matchers. Typically you want to do something that would not be possible if the expression you were interested in was evaluated and the result passed to you.
For example the raises_error matcher wants to execute the block itself to see that the correct exception is raised and the change matcher wants to evaluate some other expression before and after to see if it changes in the specified way. This is why you are passed a block rather than the value.
The block you are passing to expect takes 1 argument and uses this as the block in the call to my_each, so when your Probe.probe method calls the block it has to pass something as the value. You've phrased as "sending the block to the probe instance" but it is the other way around: the block is called using probe as its argument.
The block executes and calls my_each. Inside here p is the instance of Probe. Prefixing an argument with a & tells ruby that this argument should be used as the method's block (the method being my_each). If the argument is not already a proc ruby calls to_proc on it. This is how Probe#to_proc is called
Assuming that my_each behaves in a similar way to normal each it will call its block (ie the return value of to_proc) once for each of the values in the array.
Normally your matcher would then compare the return value from Probe.probe to the actual value.
I need some help understanding what's going on here. It's a block inside of a method. Basically I get everything here except the call in the if statement wasABlock_nowAProc.call. This is not defined here, so what is it calling?
class Array
def eachEven(&wasABlock_nowAProc)
isEven = true # We start with "true" because arrays start with 0, which is even.
self.each do |object|
if isEven
wasABlock_nowAProc.call object
end
isEven = (not isEven) # Toggle from even to odd, or odd to even.
end
end
end
['apple', 'bad apple', 'cherry', 'durian'].eachEven do |fruit|
puts 'Yum! I just love '+fruit+' pies, don\'t you?'
end
# Remember, we are getting the even-numbered elements
# of the array, all of which happen to be odd numbers,
# just because I like to cause problems like that.
[1, 2, 3, 4, 5].eachEven do |oddBall|
puts oddBall.to_s+' is NOT an even number!'
end
def eachEven(&wasABlock_nowAProc) declares that the eachEven method accepts a block, which is the do ... end stuff from your two examples. It is then accessible within the method as a .callable Proc/Lambda object with the name wasABlock_nowAProc.
wasABlock_nowAProc.call object basically invokes the attached block from within the method and passes it one argument, object.
Do a google search for "ruby block to proc" and any of the first results should give you a thorough explanation.
Basically, when you define a method parameter with an & prefix (as in def eachEven(&wasABlock_nowAProc)), it tells Ruby that this method is expecting to receive a Proc object as an argument. In the method body you can then do stuff with Proc, such as use the #call method to run it or pass it on to another method.
Now, it's rare for Ruby programmer to manually create Proc objects. It's much more common to just use a block (less typing, easier to read, etc). If you try to pass a block to method that requires a Proc, well, Ruby handles that just fine. It magically converts the block to a Proc and uses that.
In this particular example, the only reason I can see to define the &wasABlock_nowAProc parameter is to tell Ruby to raise an error if the method is called with a block. You could remove the parameter and replace the #call line with yield to achieve the same functionality.
Presenting the Idiom
I found an interesting but unexplained alternative to an accepted answer. The code clearly works in the REPL. For example:
module Foo
class Bar
def baz
end
end
end
Foo.constants.map(&Foo.method(:const_get)).grep(Class)
=> [Foo::Bar]
However, I don't fully understand the idiom in use here. In particular, I don't understand the use of &Foo, which seems to be some sort of closure, or how this specific invocation of #grep operates on the result.
Parsing the Idiom
So far, I've been able to parse bits and pieces of this, but I'm not really seeing how it all fits together. Here's what I think I understand about the sample code.
Foo.constants returns an array of module constants as symbols.
method(:const_get) uses Object#method to perform a method lookup and return a closure.
Foo.method(:const_get).call :Bar is a closure that returns a qualified path to a constant within the class.
&Foo seems to be some sort of special lambda. The docs say:
The & argument preserves the tricks if a Proc object is given by & argument.
I'm not sure I fully understand what that means in this specific context, either. Why a Proc? What "tricks," and why are they necessary here?
grep(Class) is operating on the value of the #map method, but its features are not obvious.
Why is this #map construct returning a greppable Array instead of an Enumerator?
Foo.constants.map(&Foo.method(:const_get)).class
=> Array
How does grepping for a class named Class actually work, and why is that particular construction necessary here?
[Foo::Bar].grep Class
=> [Foo::Bar]
The Question, Restated
I'd really like to understand this idiom in its entirety. Can anyone fill in the gaps here, and explain how the pieces all fit together?
&Foo.method(:const_get) is the method const_get of the Foo object. Here's another example:
m = 1.method(:+)
#=> #<Method: Fixnum#+>
m.call(1)
#=> 2
(1..3).map(&m)
#=> [2, 3, 4]
So in the end this is just a pointfree way of saying Foo.constants.map { |c| Foo.const_get(c) }. grep uses === to select elements, so it would only get constants that refer to classes, not other values. This can be verified by adding another constant to Foo, e.g. Baz = 1, which will not get grepped.
If you have further questions please add them as comments and I'll try to clarify them.
Your parse of the idiom is pretty spot on, but I'll go through it and try to clear up any questions you mentioned.
1. Foo.constants
As you mentioned, this returns an array of module constant names as symbols.
2. Array#map
You obviously know what this does, but I want to include it for completeness. Map takes a block and calls that block with each element as an argument. It returns an Array of the results of these block calls.
3. Object#method
Also as you mentioned, this does a method lookup. This is important because a method without parentheses in Ruby is a method call of that method without any arguments.
4. &
This operator is for converting things to blocks. We need this because blocks are not first-class objects in Ruby. Because of this second-class status, we have no way to create blocks which stand alone, but we can convert Procs into blocks (but only when we are passing them to a function)! The & operator is our way of doing this conversion. Whenever we want to pass a Proc object as if it were a block, we can prepend it with the & operator and pass it as the last argument to our function. But & can actually convert more than just Proc objects, it can convert anything that has a to_proc method!
In our case, we have a Method object, which does have a to_proc method. The difference between a Proc object and a Method object lies in their context. A Method object is bound to a class instance and has access to the variables which belong to that class. A Proc is bound to the context in which it is created; that is, it has access to the scope in which it was created. Method#to_proc bundles up the context of the method so that the resulting Proc has access to the same variables. You can find more about the & operator here.
5. grep(Class)
The way Enumerable#grep works is that it runs argument === x for all x in the enumerable. The ordering of the arguments to === is very important in this case, since it's calling Class.=== rather than Foo::Bar.===. We can see the difference between these two by running:
irb(main):043:0> Class === Foo::Bar
=> true
irb(main):044:0> Foo::Bar === Class
=> false
Module#=== (Class inherits its === method from Method) returns True when the argument is an instance of Module or one of its descendants (like Class!), which will filter out constants which are not of type Module or Class.
You can find the documentation for Module#=== here.
The first thing to know is that:
& calls to_proc on the object succeeding it and uses the proc produced as the methods' block.
Now you have to drill down to how exactly the to_proc method is implemented in a specific class.
1. Symbol
class Symbol
def to_proc
Proc.new do |obj, *args|
obj.send self, *args
end
end
end
Or something like this. From the above code you clearly see that the proc produced calls the method (with name == the symbol) on the object and passes the arguments to the method. For a quick example:
[1,2,3].reduce(&:+)
#=> 6
which does exactly that. It executes like this:
Calls :+.to_proc and gets a proc object back => #<Proc:0x007fea74028238>
It takes the proc and passes it as the block to the reduce method, thus instead of calling [1,2,3].reduce { |el1, el2| el1 + el2 } it calls
[1,2,3].reduce { |el1, el2| el1.send(:+, el2) }.
2. Method
class Method
def to_proc
Proc.new do |*args|
self.call(*args)
end
end
end
Which as you can see it has a different implementation of Symbol#to_proc. To illustrate this consider again the reduce example, but now let as see how it uses a method instead:
def add(x, y); x + y end
my_proc = method(:add)
[1,2,3].reduce(&my_proc)
#=> 6
In the above example is calling [1,2,3].reduce { |el1, el2| my_proc(el1, el2) }.
Now on why the map method returns an Array instead of an Enumerator is because you are passing it a block, try this instead:
[1,2,3].map.class
#=> Enumerator
Last but not least the grep on an Array is selecting the elements that are === to its argument. Hope this clarifies your concerns.
Your sequence is equivalent to:
c_names = Foo.constants #=> ["Bar"]
cs = c_names.map { |c_name| Foo.__send__(:const_get, c_name) } #=> [Foo::Bar]
cs.select{ |c| Class === c } #=> [Foo::Bar]
You can consider Object#method as (roughly):
class Object
def method(m)
lambda{ |*args| self.__send__(m, *args) }
end
end
grep is described here http://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-grep
=== for Class (which is subclass of Module) is described here http://ruby-doc.org/core-1.9.3/Module.html#method-i-3D-3D-3D
UPDATE: And you need to grep because there can be other constants:
module Foo
PI = 3.14
...
end
and you probably don't need them.