Ruby starts a new scope whenever you start a new Class, Module or Method definition (these are called “scope gates”). That is why in your first case ‘arr’ is not visible within ‘displayArr’. In the second case you are passing a reference to the value so it works.
I read about this in the book Metaprogramming Ruby by Paolo Perrotta, which discusses how to “flatten the scope”.
If writing a simple script, you could use a global variable:
I think what bothers me the most(in my posted example) is that I expected the scopes to nest in my posted example. I expected arr to be available in displayArr.
#! /usr/bin/env ruby
#encoding: utf-8
arr = [1, 2, 3,]
define_method(:displayArr) do
arr.each{|e| puts e}
end
displayArr
But this also defines the method under Kernel if defined like this.
This define_method() or define_singleton_method(), etc, are very helpful because they work as block, but def don’t. So if you need to access variables outside the scope, either use instance variables, global variables, or use define_method()
And as said above by me and pcl, you have options, you can use global variable or instance variable to do that. It’s not like you can’t absolutely do that.
$ perl -e "print 'a' x 5"
aaaaa
$ perl -e "print 'b' x 5"
bbbbb
$ perl -e "print 5 x 5"
55555
Which language use x to multiply string?
Everything is language specific, and you shouldn’t compare one to another.
JS also has let, var, const, let is more like defining local variable like Ruby, but it pollutes nested scopes. Ruby doesn’t do that. What other language use let and var to define variables? It’s just so confusing, Most of the times JS programs will work without those. People use ; in JS, even though it has Automatic Semicolon Insertion. No other ASI language ask you to use ;, in fact it’s a bad practice in Ruby or Python or in most shells.
So, if another language doesn’t have block concept like Ruby, doesn’t mean Ruby has to eliminate that. It’s been there like this forever. If you think Ruby’s syntax is confusing, try using JS for your console apps!
I think you mistook me. This isn’t a complaint about Ruby. If anything, its a complaint about me and understanding local variables in Ruby.
I just find it remarkable that I can’t understand why locals behave this way in Ruby. There must be a reason it was designed this way. They must have been designed this way for a reason and I can’t figure out what that reason is.
I’m odd this way. If I can’t understand why something behaves the way it does… I can’t understand it and use it with confidence.
There must be a reason it was designed this way. They must have been designed this way for a reason
Actually, I think the other way. Why should local variables be visible from within a method? A method definition is a “clean room” - I should be able to write a method definition and use that method anywhere without worrying about the context of local variables. Even in small scripts, I wouldn’t intermingle variable and method definitions in the way you did - it just seems unnatural to me. e.g. in Java, there is no option to declare local variables and methods in the mixed way that Ruby allows.
Allowing local variables to be visible inside methods opens up several complications:
e.g. say you gave me a method definition, and I called it. Should I have to worry about my local variable names to be able to use your method? e.g. if your method uses internally the variable i, then suddenly I cannot use the variable i because you did? How would I know that?
Or perhaps methods only “see” the local variables where they are defined, not where they are called? So I have to be careful where I copy-paste that method?
There’s a separate concept, the “closure”, which is a function which includes its local environment (local variables) at the point where it is defined. Closures are defined “on the fly”, when you need them in specific cases. You can see an example of a closure with do-end blocks, e.g.:
sum = 0
[1,2,3,4].each do |number|
sum += number
end
puts sum
The do-end closure includes the local environment so it can use sum. The do-end defines a (nameless) function that gets passed to tne each method as an argument.
As @SouravGoswami has said, different languages offer different ways of doing things and different ways of thinking about problems. Which language are you most familiar with? I suspect that language has left you thinking of programs in “one way”.
To my way of thinking, Ruby’s method definitions are perfectly natural. You get a “clean room” within the method definition, but if you really, really want to work with the local environment within your method, you have closures and the scope-flattening define_method etc techniques.
Rust works this way. You can nest a function inside another function, but it cannot see the local variables, e.g.
fn main() {
let x = 3;
fn inner() {
println!("X is {}", x)
}
inner();
}
gives error:
error[E0434]: can't capture dynamic environment in a fn item
--> fns.rs:8:29
|
8 | println!("X is {}", x)
| ^
|
= help: use the `|| { ... }` closure form instead
error: aborting due to previous error
Note it recommends using a ‘closure’ if we want to see the local scope.
In Go, you can only use a ‘nameless’ function definition, i.e. a closure, within another function:
package main
import "fmt"
func main() {
x := 3
inner := func() { fmt.Printf("X is %d\n", x) }
inner()
}
C supports shadowing of local variables, and it allows you to use a local variable in a local function definition:
#include<stdio.h>
int main(int argc, char* argv) {
int x = 3;
int inner() {
printf("X is %d\n", x);
}
inner();
}
1 Like
This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.