Project

General

Profile

Feature #19484

Updated by joel@drapper.me (Joel Drapper) about 1 year ago

Calling `binding` on a C-level proc (from `&:symbol`) raises an `ArgumentError`, "Can't create Binding from C level Proc" but there is no way to tell if a given proc is a C-level proc before calling `binding` on it. It’s possible to rescue this error, but rescuing an exception is slow. 

 Given that a C-level proc doesn't have a binding, would it make more sense to respond to `binding` with `nil` rather than raise an error? That would allow us to, for example, look up the receiver and fall back to self, e.g. `block.binding&.receiver || self`. 

 Alternatively, it would be useful to be able to check whether a given proc is a C-Level proc using something like this. 

 ```ruby 
 case block 
 when CProc 
   block.call(self) 
 else 
   block.call 
 end 
 ``` 

 An example use case is an interface that takes a hash of conditions and tokens. If the condition is valid, we do something with the token. Otherwise, we ignore it. 

 ```ruby 
 some_interface(-> { true } => "foo", -> { false } => "bar" ) 
 ``` 

 With literal Procs, it makes sense to call them and check their truthiness. 

 ```ruby 
 def some_interface(**conditional_tokens) 
   conditional_tokens.each do |condition, token| 
     do_something_with(token) if condition.call 
   end 
 end 
 ``` 

 When given C-Level procs, it makes sense to call them with `self` to evaluate the corresponding method. 

 ```ruby 
 some_interface(&:foo? => "foo", &:bar? => "bar" ) 
 ``` 

 ```ruby 
 def foo? = true 
 def bar? = false 

 def some_interface(**conditional_tokens) 
   conditional_tokens.each do |condition, token| 
     case condition 
     when CProc 
       do_something_with(token) if condition.call(self) 
     else 
       do_something_with(token) if condition.call 
     end 
   end 
 end 
 ```

Back