Cannot pass Ruby lambda with ampersand as a block - ruby

I'm coming back to Ruby after a long time away, and currently struggling with the relationship between blocks and lambdas. I know the general relationship, and I know that in principle you can pass a lambda as a block by prefixing it with an ampersand.
This works for simple examples like:
(1..5).each &->(n) { n > 0 }
But I'm now running into other cases where this doesn't work, such as if I attempt to do this with PropCheck:
PropCheck.forall(integer) &->(n) { n > 0 }
results in:
(irb):6:in `<main>': undefined method `&' for #<PropCheck::Property:0x00007fd1889af040 #config=#<struct PropCheck::Property::Configuration verbose=false, n_runs=100, max_generate_attempts=10000, max_shrink_steps=10000, max_consecutive_attempts=30>, #hooks=#<PropCheck::Hooks:0x00007fd1889aefc8 #before=#<Proc:0x00007fd1889aef78 /usr/local/lib/ruby/gems/3.0.0/gems/prop_check-0.14.1/lib/prop_check/hooks.rb:22>, #after=#<Proc:0x00007fd1889aef50 /usr/local/lib/ruby/gems/3.0.0/gems/prop_check-0.14.1/lib/prop_check/hooks.rb:22>, #around=#<Proc:0x00007fd1889aef28 /usr/local/lib/ruby/gems/3.0.0/gems/prop_check-0.14.1/lib/prop_check/hooks.rb:22>>, #gen=#<PropCheck::Generator:0x00007fd1889af2c0 #block=#<Proc:0x00007fd1889af270 /usr/local/lib/ruby/gems/3.0.0/gems/prop_check-0.14.1/lib/prop_check/generators.rb:84>>> (NoMethodError)
from /usr/local/Cellar/ruby/3.0.1/lib/ruby/gems/3.0.0/gems/irb-1.3.5/exe/irb:11:in `<top (required)>'
from /usr/local/opt/ruby/bin/irb:23:in `load'
from /usr/local/opt/ruby/bin/irb:23:in `<main>'
So clearly I'm missing some subtlety about passing lambdas as blocks. Why doesn't this work? Is there some general thing I'm not aware of that sometimes makes passing lambdas as blocks unacceptable?

Thanks to Sergio and engineersmnky, what was really happening was that I was misunderstanding argument lists of both regular and block parameters. So I can instead do, as suggested:
PropCheck.forall(integer, &->(n) { n > 0 })
UPDATE: Indeed, I see the second option which seemed to work for me in a quick irb session does not do the right thing (PropCheck.forall integer &->(n) { n > 0 }), so the right answer is above.

Related

'&:' usage with 'is_a(Integer)'

I would like this code:
[1,2,3].all? {|x| x.is_a?(Integer)}
to work using the &: method like:
[1,2,3].all?(&:is_a?(Integer))
but I get this error:
syntax error, unexpected '(', expecting ')'
I guess it's because I am calling is_a?(Integer) as a symbol.
How can I pass Integer to :is_a??
It is impossible. You cannot pass Integer (or anything else) to a symbol :is_a?. A symbol does not take an argument. No object in Ruby takes an argument (without a method call).
By the way, there is no such thing as &:.
You can get close to the notation you want with a lambda:
is_an_int = ->(o) { o.is_a?(Integer) }
[1,2,3].all?(&is_an_int)
or even closer, a lambda which returns a lambda:
is_a = ->(c) { ->(o) { o.is_a?(c) } }
[1,2,3].all?(&is_a[Integer])
Possibly more trouble than it is worth in this case but useful techniques none the less.

Scala -> Ruby conversion: Confusing output

Ruby Code:
def fnAdd(x)
return ->(y) { x + y }
end
add1 = 1.method(:fnAdd)
puts add1.call(10)
Output: Proc:0x007f52658d3330#main.rb:2 (lambda)
I am having issues getting the desired output in the above code.
I'm basically trying to write the following Scala code (which calls a function that returns another function) in Ruby.
Scala Code:
def fnAdd (x:Int) = {
(y:Int) => x + y
}
var add1 = fnAdd (1)
var add2 = fnAdd (2)
println(add1(10))
println(add2(3))
Output: 11 5
I've made an attempt at converting the code to Ruby but I'm not sure if it is correct. I don't understand the output, which appears to be some kind of proc object.
Could someone please explain what I need to change to get the desired output?
I'm not sure how your first example is even running, as it produces a NameError on my machine. Regardless, #method is intended for accessing methods on specific objects. You've defined a standalone method which is already curried, not one inside of the Fixnum class. So you simply need to call it as a method.
add1 = fnAdd(1)
Also, Ruby has the same behavior as Scala with regard to returning the last expression in a method, so you don't need to use return in this case.
Edit:
Thanks to #JörgWMittag for pointing out a few flaws here. Defining #fnAdd at the top-level makes it a private instance method on Object. Since everything in Ruby is an object, Fixnum inherits from the Object class. Thus, 1.method(:fnAdd) is simply giving you the fnAdd method without actually passing it any arguments. Thus, it still expects to be called twice.
fnAddMethod = 1.method(:fnAdd)
add1 = fnAddMethod.call(1)
puts add1.call(10)
However, this would be extremely unidiomatic, so it's best to stick with the simpler solution.

Ruby: negate hash passed to proc?

In ruby, the following expression:
x.filter {|n| n.even?}
can also be written as:
x.filter(&:even?)
so, I am wondering how I would write this expression?
x.filter {|n| !n.even?}
without using odd? method
As Sam and engineerskmnky said in the comments below question, it is not possible to perform x.filter { |n| !n.even? } operation directly (and in fact two operations inside the block).
I guess that this was only a trivial example and not a real code so if you have method that does not have the inverse one and you don't want to create one, you can create a lambda or proc in the following way:
not_even = -> (n) { !n.even? }
and then call it on filter as:
x.filter(&not_even)
You can also use reject method which should give you the same result without the magic of using lambda.

Using Ruby to solve a quiz

So I found this quiz on a website that I was excited to solve with my newly acquired Ruby skills (CodeAcademy, not quite finished yet).
What I want to do is make an array with 100 entries, all set to "open". Then, I planned to create a method containing a for loop that iterates through every nth entry of the array and changes it to either "open" or "closed", based on what it was before. In the for loop, n should be increased from 1 to 100.
What I have so far is this:
change_state = Proc.new { |element| element == "open" ? element = "closed" : element = "open" }
def janitor(array,n)
for i in 1..n
array.each { |element| if array.index(element) % i == 0 then element.change_state end }
end
end
lockers = [*1..100]
lockers = lockers.map{ |element| element = "closed" }
result = janitor(lockers,100)
When trying to execute I receive an error saying:
undefined method `change_state' for "closed":String (NoMethodError)
Anybody an idea what is wrong here? I kinda think I'm calling the "change_state" proc incorrectly on the current array element.
If you know the quiz, no spoilers please!
As you have implemented change_state, it is not a method of any class, and definitely not one attached to any of the individual elements of the array, despite you using the same variable name element. So you cannot call it as element.change_state.
Instead, it is a variable pointing to a Proc object.
To call the code in a Proc object, you would use the call method, and syntax like proc_obj.call( params ) - in your case change_state.call( element )
If you just drop in that change, your error message will change to:
NameError: undefined local variable or method `change_state' for main:Object
That's because the change_state variable is not in scope inside the method, in order to be called. There are lots of ways to make it available. One option would be to pass it in as a parameter, so your definition for janitor becomes
def janitor(array,n,state_proc)
(use the variable name state_proc inside your routine instead of change_state - I am suggesting you change the name to avoid confusing yourself)
You could then call it like this:
result = janitor(lockers,100,change_state)
Although your example does not really need this structure, this is one way in which Ruby code can provide a generic "outer" function - working through the elements of an array, say - and have the user of that code provide a small internal custom part of it. A more common way to achieve the same result as your example is to use a Ruby block and the yield method, but Procs also have their uses, because you can treat them like data as well as code - so you can pass them around, put them into hashes or arrays to decide which one to call etc.
There may be other issues to address in your code, but this is the cause of the error message in the question.

calling inject only passing it a symbol

Could someone explain this with an example of how it is useful.
Specifically I am having an issue with how you accumulate something (an array would be nice) when you change scope into a method..
e.g.
def modify(value)
...code ....
end
an_array.inject(:modify)
How can I get an accumulator (above), or something that is passing a message along. The value returned is the last value to come out of modify. But modify only gets each value in the array. It doesn't get the message. (below) passing message to the .... code ...
an_array.inject(0) { |message,element| .... code .... }
Using inject with only a symbol:
[*1..5].inject(:+) #=> 15
The above translates to:
[*1..5].inject { |sum, num| sum + num }
This is what the docs have to say on the subject (emphasis added):
Combines all elements of enum by applying a binary operation,
specified by a block or a symbol that names a method or operator
I'm happy to update my answer once you clarify the second part of your question a bit (e.g. what do you mean by "changing scope" in this context?).

Resources