In Ruby, what does a top-level assignment do? - ruby

I have the following code at the top-level of a .rb file:
class Times
def initialize(n)
#n = n
end
def each()
(1..#n).each {yield}
end
end
three_times = Times.new(3)
def f()
Times.new(3).each {puts 'Test'}
end
f()
This works, and prints 'Test' three times, as expected. However, if I replace Times.new(3) in f with three_times, i.e. three_times.each {puts 'Test'}, I get an error:
`f': undefined local variable or method `three_times' for main:Object (NameError)
Why does this not work? Why is Times accessible from within f, but not three_times?
More generally, what exactly does an assignment at the top level (e.g. three_times = Times.new(3)) do?

Because
three_times is a local variable
local variables are only accessible inside of a specific scope
def in ruby creates a new scope
So when f is invoked, it does not see or have access to three_times
To access three_times change it to either a global variable $three_times or an instance variable #three_times
The reason that you are able to reference the class Times is that it is a constant and ruby goes through a separate process of lookup for constants.
Sidestepping issue with def
You could also access the local variable by using a block to define your method, which sidesteps the whole scope gate issue. I do this sometimes when writing rake tasks but rarely do it outside of scripts.
three_times = Times.new(3)
define_method :foo do
three_times.each { puts 'Tests'}
end
foo

Why does this not work? Why is Times accessible from within f, but not three_times?
Variables whose name starts with a lowercase letter are local variables. Local variables are local to the scope they are defined in (that's why they are called local variables.)
Variables whose name starts with an uppercase letter are constants. Constants are looked up first in the default constant scope, then lexically outwards, then dynamically upwards by inheritance.
More generally, what exactly does an assignment at the top level (e.g. three_times = Times.new(3)) do?
Nothing special. It does the same thing that an assignment anywhere else does. In this case, it:
Dereferences the variable (constant) Times, let's call this object o1.
Evaluates the literal integer expression 3, let's call the resulting object o2.
Sends the message new to o1, passing o2 as an argument. Let's call the answer to that message send o3.
Binds o3 to the local variable named three_times.
As you can see, there's nothing in there that is somehow specific to script scope or the top-level.

It's because it's looking for a local variable called "three_times". If you wish to make "three_times" to be "top-level" or "global", prepend the variable name with $ so that it's "$three_times".

Your code works for me, there is no errors. You can call f() sucecsfully

Related

Why we are using _ prefix in some cases to define instance variables in ruby?

I see in ruby classes some people using _ prefix to define instance variables like #_property while in normal condition attr_writer uses the normal name like #property to define instance variables.
What is the point of doing this and what is the difference?
The _ prefix isn't a language feature, it doesn't do anything special, it is just a convention.
In most cases, the developer wants to indicate that a variable is not in use, for example, a block variable that is not used in a block. Or they want to indicate that the variable is an internal variable of a module or gem and that others should not read or modify this variable directly.
There are 2 possible answers here depending on type of a variable.
Instance variable
When _ is used to prefix an instances variable (like in your question) it is usually just a convention to make it clear that that instance variable is private implementation detail and should not be used outside of the current scope. You might encounter it especially in modules which are to be included in some other classes - in which case the instance variable is defined and used by that module, but it belongs and is scoped to the object itself. I personally prefer object-scoped registries for this, but "private" instance variable is a quick, dirty and popular way to go. Alos, prefixing instance variable name with _ reduces chances of name conflict.
# Library code
module SomeExternalLibraryModule
def foo
#_something ||= SomeExternalLibraryModule::Something.new(self)
end
end
# Application code
class User
include SomeExternalLibraryModule
attr_reader :something # name conflict "avoided"! Phew!
def bar
#_something.do_sth
# "_" means - you'd better know what you're doing here!
# There is no guarantee that instance variable will be present in the future release
end
end
Local variable
When local variable is prefixed with _ this means that that variable is not being used in the code. It is especially useful when using iterators, or consuming other multi-element inputs of which you're interested in only one.
It is quite common to see just _ as a variable name. It has a bit of a special meaning for parser, which is you explicitly saying "I do not care what that is". As such, it is the only argument name that is allowed multiple time in a single definition:
def foo(_, _, a)
a
end
foo(1,2,3) #=> 3
def bar(a,a,b); end #=> SyntaxError
However, this is usually the best practice to use "I do not care" with a name, which will make your life easier in the future if you actually decide that you need to use other arguments:
def foo(_name, _html_options, options)
options.delete(:some_option)
super
end

Ruby: Unable to access global variable, not starting with $

Normally every global variable in ruby ​​should start with a $. Are these parameters "param_string" and "param_array" also global variables or something else?
require 'json'
require 'fileutils'
param_string=String.new
param_array=Array.new
# global variable
$tester="hello"
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: staging.rb [options]"
opts.on("-s","--string TEST") do |test|
puts "test string #{test}"
param_string << test
end
end.parse!
def string_funktion()
puts $tester
# working as expected
puts "show my string #{param_string}"
# error: undefined local variable
end
Attempting to access one of these parameters "param_string" or "param_array" within a function and get the error message that the parameter is not defined. If I do the same with the variable $tester, the access works as desired.
What do I have to change for accessing the parameters "param_string" or "param_array" within a function?
EDIT:
Are there reasons or best practices for globally defined variables without naming $?
Normally every global variable in ruby ​​should start with a $.
This is wrong. This has nothing to do with "should".
If it starts with a $, then it is a global variable. If it doesn't, then it is not a global variable.
Are these parameters "param_string" and "param_array" also global variables or something else?
Variables that start with a lowercase letter are local variables. Local variables are called "local" variables, because they are local to the scope they are defined in. In this case, they are defined in the script body, so they are local to the script body.
There are 4 different scopes in Ruby:
Script
Module / Class definition
Method definition
Block / lambda literal
Block scopes are special, because they can be closures and are nested.
Attempting to access one of these parameters "param_string" or "param_array" within a function and get the error message that the parameter is not defined. If I do the same with the variable $tester, the access works as desired.
[Note: this is not a function, it is a method. Ruby doesn't have functions.]
param_string and param_array are local variables, which you can see because they start with a lowercase letter. Local variables are local to the scope they are defined in, in this case the script scope. Therefore, they are not defined in the method.
$tester is a global variable, which you can see because it starts with a $ sign. Global variables are, as the name implies, global. Therefore, it can be accessed everywhere. (Note that there are some builtin "magic" global variables like $! or $1…$9 that behave slightly differently. As a beginner, it is probably easiest to ignore those slight differences for now.)
What do I have to change for accessing the parameters "param_string" or "param_array" within a function?
You could make them global variables, but in general, global variables should be avoided. (Not just in Ruby.)
What the best design in your case would be is hard to tell from the code you provided. You could pass them into the method as arguments, but it is also possible that your design is missing an object or two.
Best practice would be to pass the local variables to the function as parameters.
def string_funktion(param_string, param_array)

how to use truly local variables in ruby proc/lambda

Beginner Ruby question. What is the simplest way to change this code, but leaving the block completely intact, that eliminates the side effect?
x = lambda { |v| x = 2 ; v}
x.call(3)
#=> 3
x
#=> 2
This is the simplest example I could contrive to illustrate my issue, so "remove the assignment" or "don't assign the Proc to x" is not what I'm looking for.
I want to set local variables in a Proc (or lambda) that can be assigned without affecting the original enclosing scope. I could dynamically create a class or module to wrap the block, but that seems overkill for such a basic thing.
Equivalent Python to what I'm trying to do:
def x(v):
x = 2 # this is a local variable, what a concept
return v
Sometimes it is the desired behavior:
total = 0
(1..10).each{|x| total += x}
puts total
But sometimes it's accidental and you don't want to mess with an outside variable which happens to have the same name. In that case, follow the list of parameters with a semicolon and a list of the block-local variables:
x = lambda{|v; x| x = 2; v}
p x.call(3) #3
p x #<Proc:0x83f7570#test1.rb:2 (lambda)>
The reason for this is that the lambda is bound to its defining scope (NOT its calling scope), and is a full closure, which, among other things, includes the local variable x. What you really want here is an unbound proc to pass around and call without any particular binding. This isn't something that Ruby does very easily at all, by design (it's possible with eval, but that's less of a block and more just of a string statement). Procs, lambdas, and blocks are all bound to their defining scope. Lexical scope is only established on classes, modules, and methods; not on blocks/procs/lambdas/anything else.
It's worth noting that Python doesn't even permit assignment in lambdas in the first place.

Why can I refer to a variable outside of an if/unless/case statement that never ran?

Why does the following code not throw an error?
if false
x = 0
end
x #=> nil
Whereas the following does throw an error:
y # NameError: undefined local variable or method `y' for main:Object
The same thing happens with unless & case statements.
It's because of how the Ruby parser works. Variables are defined by the parser, which walks through the code line-by-line, regardless of whether it will actually be executed.
Once the parser sees x =, it defines the local variable x (with value nil) henceforth in the current scope. Since if/unless/case/for/while do not create a new scope, x is defined and available outside the code block. And since the inner block is never evaluated as the conditional is false, x is not assigned to (and is thus nil).
Here's a similar example:
defined?(x) and x = 0
x #=> nil
Note that this is a rather high-level overview of what happens, and isn't necessarily exactly how the parser works.
This has to do with a quirk of Ruby's scoping rules.
In ruby, an undecorated variable x appearing by itself could either be a local variable or a method call -- the grammar can't tell which. It's up to the parser to figure it out as it resolves local variable references. The rule is simple: if an assignment to a variable of the same name has been seen already in the local scope, then the reference is a local variable, and the reference is bound to that local variable. Otherwise, it's a method call, and it will be looked up as such at runtime.
Local variable references in Ruby are optimized into array lookups (each local variable is assigned a 'slot', and bound local variable references generated by the parser are converted into slot references). The array is initialized with all nil:
/* initialize local variables */
for (i=0; i < local_size; i++) {
*sp++ = Qnil;
}
Thus, if you refer to a local variable that hasn't been assigned, through a bound local reference (which can only happen if there was a skipped assignment above the reference in the same local scope), you get nil.
I thought your question was interesting so I tried to look it up and found this:
I don't understand ruby local scope
The correct answer seems to be put Jorg.
Lets look at what happens when you try to access a variable that isn't initialized:
NameError: undefined local variable or method `UNDECLAREDVAR' for main:Object
The exception states that it is unavailable to evaluate whether a variable or method. The reason it doesn't throw the same exception is because uninitialized local variables are set to nil. So puts x is a okay because the interpretor knows that x is variable but uninitialized and not a method.

Redundancy of notation in instance_variable_set, instance_variable_get

When I set or get instance variables using some name, for example #foo, I can do something like:
instance_variable_set("#foo", some_value)
...
instance_variable_get("#foo")
But often, I use a variable for the method name, which does not include the # prefix, so that I end up doing:
method = :foo
...
instance_variable_set("##{method}", some_value)
...
instance_variable_get("##{method}")
But since all instance variables are prefixed with #, I think it redundant to have to type "##{method}" instead of simply typing method. Why are the methods instance_variable_set and instance_variable_get not designed to accept string/symbol without # as its first argument like this:
method = :foo
...
instance_variable_set(method, some_value)
...
instance_variable_get(method)
where the variable to be actually set will be #foo rather than foo?
Is there any advantage with the way it is?
The reason is, quite simply, that the instance variable is named #foo, not foo. The # is part of the variable name, just as the $ is part of the global variable name $foo.
The reason that # is not necessary when calling attr_accessor and friends is because they define attribute methods, so it makes sense to provide the method names, not the variable names.
Of course there is no technical reason instance_variable_set cannot prepend the # itself. However, the method accepts a symbol that corresponds to the variable name. A symbol by definition represents the identifier with the given name. So the only symbol that corresponds to the instance variable #foo is :#foo. That is why you have to include the #, because we know that :foo does not correspond to any instance variable identifier at all. (And if you supply a string, it will be converted to a symbol internally first.)
Update: In the C Ruby implementation (MRI), there is actually no mention of # anywhere in the code that handles instance variables. Only the parser knows instance variables start with a #. So it seems that separating code parsing from implementation is another possible reason.

Resources