Instance Functions vs Class Functions in Ruby - ruby

I have two versions of a simple program that accomplishes the same thing. I have some questions.
When should I use class functions over instance functions?
Are there any performance benefits of using class vs instance functions? Does one use more resource than the other given everything else constant.
Is defining a class function with self the same as defining it with the module's name (version 2 question)
Version 1: instance functions
I have a file called pastries.rb that contains:
module Pastries
def description
return "I am a pastry!"
end
end
Then in another file called main.rb, I have:
require 'pastries'
include Pastries
puts "With include: #{description}" # With include: I am a pastry!
Version 2: class functions
pastries.rb:
module Pastries
# Are the following equivalent?
# def self.info
# return "I am a pastry!"
# end
def Pastries.description
return "I am a pastry!"
end
end
main.rb:
require 'pastries'
puts "WITHOUT include: #{Pastries.description}" # WITHOUT include: I am a pastry!
Any feedback is appreciated. I am a newbie both on stackoverflow and in Ruby, so correction of posting style or other critiques are welcomed as well.

To start off, functions in Ruby are called Methods.
In version 1, you are using the Module with an instance method, which can be used in mixins (i.e., it is mixed in to a class) as above. Here, you are mixing it with the top-level object class in main.rb.
In version 2, you are using the Module(s) with class method(s), where the module acts as a namespace to avoid namespace collisions for these methods when it is used along with other modules and classes.
To your particular questions:
1. When should I use class functions over instance functions?
Class level methods within modules may be used when you want to provide direct access to a module's methods without the need of instantiating a class object (for ex. standalone libraries) or when you want to use the method in the scope of the object where it is mixed in.
Instance methods should be used when you want to call the module's method via a class object where it is mixed in (for ex. to provide additional functionality for a class and serve a mechanism for a class similar to multiple inheritance)
2. Are there any performance benefits of using class vs instance functions? Does one use more resource than the other given everything else constant.
As explained in 1 and 3, the benefits depend on the required usage. For memory consumption, AFAIK, no countable differences.
3. Is defining a class function with self the same as defining it with the module's name (version 2 question)
Not exactly. When you define a module method with module's name, it'll always be accessible in the scope of the module, while a module method defined with self will be accessed in the scope of the object from where it has been called. See the following answer for a detailed description: Trying to understand use of self.method_name vs. Classname.method_name in Ruby
Hope this helps, Cheers.

Related

Ruby inheritance and typing

I am having trouble with some fundamental concepts in Ruby, specifically the interchangeability of a subclass for the superclass.
According to the Ruby documentation on classes, "Class" inherits from "Module".
https://ruby-doc.org/core-2.5.3/Class.html
class MyClassTest end
MyClassTest.is_a? Module # => true
However, when trying to use the module keyword to reopen a class defined with the keyword class, you get a TypeError that the class is not a module.
class MyClassTest end
module MyClassTest end # => TypeError: MyClassTest is not a module
This SO question has some excellent discussion surrounding subclasses vs subtypes, but I think it has lead me to more questions:
Why can't classes be used as modules?
Generally, since Ruby is dynamically typed, I am confused by the existence of TypeErrors.
Specifically, in this case, I am extra confused as to how Ruby inheritance can result in a TypeError where the subclass cannot be substituted for the superclass. In my mind, subclassing is equivalent to subtyping in Ruby since the subclass will inherit the interface (methods and public attributes) of the superclass.
My current guess is that TypeError's are raised by the core Ruby library when certain assertions fail, and these TypeErrors don't necessarily have anything to do with Ruby's dynamic typing system, which is to say that typing is not a first-class concept in Ruby. The linked SO question raises excellent points about the diamond problem with multiple class inheritance, so it makes sense that Ruby would prevent the interchangeable usage of modules and classes when using the module or class keyword. Still, it feels like there are inconsistencies in my understanding of Ruby.
How can a "Class" input result in a TypeError when a "Module" object is expected?
Basic assertions are
Class is a Class (Class.is_a?(Class) #=> true)
Class is a Module (Class.is_a?(Module) #=> true)
An instance of the class Class is a Class (Class.new.is_a?(Class) #=> true)
An instance of the class Class is a Module (Class.new.is_a?(Module) #=> true)
Module is a Class (Module.is_a?(Class) #=> true)
By virtue Module is a Module (Module.is_a?(Module) #=> true)
An instance of the class Module is a Module (Module.new.is_a?(Module) #=> true)
However An instance of the class Module is not a Class (Module.new.is_a?(Class) #=> false)
an instance of the class Class is an instance of Class but not and instance of the class Module (Class.new.instance_of?(Module) #=> false)
module is a declaration for an instance of the class Module just as class is a declaration for an instance of the class Class.
If this were a method it might look like
def module(name,&block)
raise TypeError if defined?(name) && !const_get(name).instance_of?(Module)
obj = defined?(name) ? const_get(name) : const_set(name, Module.new)
obj.instance_eval(&block)
end
TypeError exists to prevent ambiguity.
As in your case by using class MyClassTest you have created an instance of the class Class and that instance is called MyTestClass.
If you were also allowed to use module MyTestClass, in the same global context, then during usage I would be unaware if when calling MyClassTest I would be calling the Class or the Module.
The basic (very basic) difference is a Class can be instantiated (have instances) however a Module cannot.
For instance
Class.new.new #creates an instance of the anonymous class created by Class.new
Module.new.new # results in NoMethodError
I think the first point of confusion is the distinction between usage and definition.
The following code defines a class:
class C; end
If I see the above code, I expect to be able to instantiate C later:
C.new
However, imagine that C was already defined as a module:
# In one file:
module C; end
# Later in a different file:
class C; end # C is actually a module
C.new # fails
Ruby surfaces the problem (conflicting definitions of C) at the point where C is redefined as a class, rather than allowing the program to continue to where C is used.
The benefit of surfacing the problem earlier is generally the earlier an error is identified, the easier it is to find and fix its root cause (in this example, perhaps C is supposed to be a class after all, and thus the real issue is the module C definition).
Your second question is, I think, why a class cannot always be used as a module, for example why the following is prohibited:
class C; end
class A
include C
end
The answer, I think, is the programming language starts with concepts which are then implemented using various constructs. We could describe classes and modules as follows:
A class represents an object which has data and behavior (classic OOP definition).
A module is a collection of behavior.
The include keyword extends a class with a module's behavior. In principle it is possible to take just a class's methods and add them to another class. But this operation doesn't make sense because a class is an object and its behavior together. Taking just the behavior goes against the concept of the class.
There are other programming languages which take different positions on this issue. For example, in JavaScript any function can be taken out of any class and invoked with an instance of any other class. This can be convenient in some situations and difficult to debug in others.

What criteria justifies using a Module over a Class in Ruby?

I'm reading my ruby book. Looking at the code below,
module Destroy
def destroy(anyObject)
#anyObject = anyObject
puts "I will destroy the object: #{anyObject}"
end
end
class User
include Destroy
attr_accessor :name, :email
def initialize(name,email)
#name = name
#email = email
end
end
my_info = User.new("Bob","Bob#example.com")
puts "So your name is: #{my_info.name} and you have email #{my_info.email}"
user = User.new("john","john#example.com")
user.destroy("blah")
I could've just created another method inside my class. Why would I want to do this? Why would I want to use a module? It's not like embedding this into other classes is any easier than just using normal inheritance.
You can think of a module and the methods and constants inside of it as more of providing utility functions and actions that you can include to other objects as you see fit. For example, if you wanted to use destroy function in the objects Foo and Bar you would do similarly:
class Foo
include Destroy
# other code below
end
class Bar
include Destroy
# other code below
end
Now any Foo or Bar object has access to all the methods or constants inside of destroy.
Modules define a namespace, a sandbox in which your methods and constants can play without having to worry about being stepped on by other methods and constants. The ruby docs goes into more depth about this and includes a good example practical of when you would want to use it as seen below:
module Debug
def whoAmI?
"#{self.type.name} (\##{self.id}): #{self.to_s}"
end
end
class Phonograph
include Debug
# ...
end
class EightTrack
include Debug
# ...
end
ph = Phonograph.new("West End Blues")
et = EightTrack.new("Surrealistic Pillow")
ph.whoAmI? » "Phonograph (#537766170): West End Blues"
et.whoAmI? » "EightTrack (#537765860): Surrealistic Pillow"
In this example, every class that includes Debug has access to the method whoAmI? and other methods and constants that Debug includes without having to redefine it for every class.
Some programming languages such as C++, Perl, and Python allow one class to inherit from multiple other classes; that is called multiple inheritance. Ruby does not support multiple inheritance. That means each class can only inherit from one other class. However, there are cases where a class would benefit by acquiring methods defined within multiple other classes. That is made possible by using a construct called module.
A module is somewhat similar to a class, except it does not support inheritance, nor instantiating. It is mostly used as a container for storing multiple methods. One way to use a module is to employ an include or extend statement within a class. That way, the class gains access to all methods and objects defined within the module. It is said that the module is mixed in the class. So, a mixin is just a module included in a class. A single module can be mixed in multiple classes, and a single class can mix in multiple modules; thus, any limitations imposed by Ruby's single inheritance model are eliminated by the mixin feature.
Modules can also be used for namespacing. That is explained in this post at the Practicing Ruby website.
You are writing a module in the same file as the class, but not necessarily inside the class, but anyway.
For me there are 3 reasons to use a module(more details here):
Modules provide a namespace and prevent name clashes.
Modules implement the mixin facility.
When you have a very complex and dense class, you can split it into
modules and include them into the main class.
Your example is fairly simple, it would indeed make more sense to write the method in the class itself, but try to imagine a complex scenario.

ways to define a global method in ruby

I'm writing a small gem, and I want to define a DSL-like method, pretty much the same as the desc and task methods in Rake.
Rake defines them as private methods in the Rake::DSL module and then
self.extend Rake::DSL
to mix the module into the main object? (I'm a newbie and go ahead laugh if I'm wrong)
what are the benefits by doing so? is it because making these methods private can prevent any other objects to use them (that is, to prevent something like some_obj.desc) ?
what if I define the methods in Kernel
module Kernel
private
include Rake::DSL
end
Is there any difference?
If you define private method in Kernel module it will be available in the whole project. You will also rewrite desc method that project use to define rake task. But if you write your methods in your submodule and then extend it in superclass or some module - you can easily write any kind of DSL lang like you might saw in Rake or RSpec.
P.S. Making methods private prevents other moludes or classes (but not subclasses) to use them (but not owerwrite) - I mean module nesting hierarchy.
Just to extend the answer given by bor1s, about the private methods:
In ruby you have "private" and "protected" methods. What bor1s says, is correct when talking about "protected" methods. Declaring a method "private" additionally prevents other instances of the same class from using the method.
When you call a "private" method, you cannot use a dot in front of it - you cannot even use self., even though using or omitting self has usually the same effect.
class Xyzzy
private
def foo
puts "Foo called"
end
public
def do_it
foo # <= Is OK
self.foo # <= raises NoMethodError
end
end
Xyzzy.new.do_it
If you change 'private' to 'protected' in the code above, no error will be raised.
And about modules:
The final result of defining a method in Kernel and extending Kernel with the method defined in some module is the same: in both cases the method is global.
Using a module is just a little more elegant, as it groups your changes in one place, but I would say it's a matter of personal taste.
Usually you do not include methods in Kernel or Object (as it may be a little dangerous), but you include (or extend) a specific class or object which needs these methods, and in this case you need your methods grouped in a module.
Even Rake in version 0.9.0 stopped including the DSL commands in Object:
== Version 0.9.0
Incompatible *change*: Rake DSL commands ('task', 'file', etc.) are
no longer private methods in Object. If you need to call 'task :xzy' inside
your class, include Rake::DSL into the class. The DSL is still available at
the top level scope (via the top level object which extends Rake::DSL).

When should I use "class Object", "class Module", "module Kernel" and nothing?

I'm new to ruby metaprogramming, and I see people metaprogramming code in different places, like class Object, class Module, module Kernel and "nothing" (ie, out of a class/module definition block).
E.g.: I'm creating a c_attr_accessor method to access class variables, and I'm not sure where I must put the code, since it works in any of those cases.
How to decide what place is more appropriate to put a new global code?
Each of these examples fall into different cases.
If you are writing methods that apply to all objects, then you open the Object class so all objects can access it. If you are writing methods that apply to all modules, then you open Module. Whenever you open a class to add methods, the methods should apply to all instances of the class and nothing else.
Extending the Kernel module is different: people do this to add methods that should be available to every scope, but not really as methods to be explicitly called on an object, by making them private.
When you are outside of any class or module statement, you are in the scope of the main object, and methods you define default to being private methods of Object. This is fine for small or simple programs, but you will eventually want to use proper modules as namespaces to organize your methods.
As a final note on the subject, you always need to be sure that you really want methods you add to built-in classes and modules to be available to everything in your application, including external inclusions because they all share the built-ins.
Now to apply this to answer your question. Because you are defining a method that creates accessors for class variables, you should put it in the class Class as it applies to all classes and nothing else. Finally, you will likely only use it in class definitions (within a class statement), so we should make it private:
class Class
private
def c_attr_accessor(name)
# ...
end
end
class User
c_attr_accessor :class_variable_name
# ...
end
If you don't really need it in every class (maybe just a few), then create a "mixin module" to extend every class that needs this feature:
module ClassVariableAccessor
private
def c_attr_accessor(name)
# ...
end
end
class User
extend ClassVariableAccessor
c_attr_accessor :class_variable_name
# ...
end
Note that you are using Object#extend to add c_attr_accessor only to the object User (remember that classes are objects; you will hear that a lot if you are new to Ruby metaprogramming).
There is another way to implement the last example, which works by explicitly extending its base class through the Module#included(base_class) "hook method" called whenever the module included, and the base class is passed to base_class:
module ClassVariableAccessor
def included(base_class)
base_class.extend ClassMethods
end
module ClassMethods
def c_attr_accessor(name)
# ...
end
end
end
class User
include ClassVariableAccessor
c_attr_accessor :class_variable_name
# ...
end
I recommend this last solution because it is the most general and uses a simple interface that should not need to be updated. I hope this is not too much!
Have you tried looking up where the normal attribute accessors are defined? I'd either define it in the same class/module, or create my own module in which all my new methods go.

What is the difference between include and extend in Ruby?

Just getting my head around Ruby metaprogramming. The mixin/modules always manage to confuse me.
include: mixes in specified module methods as instance methods in the target class
extend: mixes in specified module methods as class methods in the target class
So is the major difference just this or is a bigger dragon lurking?
e.g.
module ReusableModule
def module_method
puts "Module Method: Hi there!"
end
end
class ClassThatIncludes
include ReusableModule
end
class ClassThatExtends
extend ReusableModule
end
puts "Include"
ClassThatIncludes.new.module_method # "Module Method: Hi there!"
puts "Extend"
ClassThatExtends.module_method # "Module Method: Hi there!"
extend - adds the specified module's methods and constants to the target's metaclass (i.e. the singleton class)
e.g.
if you call Klazz.extend(Mod), now Klazz has Mod's methods (as class methods)
if you call obj.extend(Mod), now obj has Mod's methods (as instance methods), but no other instance of of obj.class has those methods added.
extend is a public method
include - By default, it mixes in the specified module's methods as instance methods in the target module/class.
e.g.
if you call class Klazz; include Mod; end;, now all instances of Klazz have access to Mod's methods (as instance methods)
include is a private method, because it's intended to be called from within the container class/module.
However, modules very often override include's behavior by monkey-patching the included method. This is very prominent in legacy Rails code. more details from Yehuda Katz.
Further details about include, with its default behavior, assuming you've run the following code
class Klazz
include Mod
end
If Mod is already included in Klazz, or one of its ancestors, the include statement has no effect
It also includes Mod's constants in Klazz, as long as they don't clash
It gives Klazz access to Mod's module variables, e.g. ##foo or ##bar
raises ArgumentError if there are cyclic includes
Attaches the module as the caller's immediate ancestor (i.e. It adds Mod to Klazz.ancestors, but Mod is not added to the chain of Klazz.superclass.superclass.superclass. So, calling super in Klazz#foo will check for Mod#foo before checking to Klazz's real superclass's foo method. See the RubySpec for details.).
Of course, the ruby core documentation is always the best place to go for these things. The RubySpec project was also a fantastic resource, because they documented the functionality precisely.
#include RubySpec rubydoc
#included RubySpec rubydoc
#extend RubySpec rubydoc
#extended RubySpec rubydoc
#extend_object RubySpec rubydoc
#append_features RubySpec rubydoc
What you have said is correct. However, there is more to it than that.
If you have a class Klazz and module Mod, including Mod in Klazz gives instances of Klazz access to Mod's methods. Or you can extend Klazz with Mod giving the class Klazz access to Mod's methods. But you can also extend an arbitrary object with o.extend Mod. In this case the individual object gets Mod's methods even though all other objects with the same class as o do not.
That's correct.
Behind the scenes, include is actually an alias for append_features, which (from the docs):
Ruby's default implementation is to
add the constants, methods, and module
variables of this module to aModule if
this module has not already been added
to aModule or one of its ancestors.
When you include a module into a class, the module methods are imported as instance methods.
However, when you extend a module into a class, the module methods are imported as class methods.
For example, if we have a module Module_test defined as follows:
module Module_test
def func
puts "M - in module"
end
end
Now, for include module. If we define the class A as follows:
class A
include Module_test
end
a = A.new
a.func
The output will be: M - in module.
If we replace the line include Module_test with extend Module_test and run the code again, we receive the following error: undefined method 'func' for #<A:instance_num> (NoMethodError).
Changing the method call a.func to A.func, the output changes to: M - in module.
From the above code execution, it is clear that when we include a module, its methods become instance methods and when we extend a module, its methods become class methods.
All the other answers are good, including the tip to dig through RubySpecs:
https://github.com/rubyspec/rubyspec/blob/master/core/module/include_spec.rb
https://github.com/rubyspec/rubyspec/blob/master/core/module/extend_object_spec.rb
As for use cases:
If you include module ReusableModule in class ClassThatIncludes, the methods, constants, classes, submodules, and other declarations gets referenced.
If you extend class ClassThatExtends with module ReusableModule, then the methods and constants gets copied. Obviously, if you are not careful, you can waste a lot of memory by dynamically duplicating definitions.
If you use ActiveSupport::Concern, the .included() functionality lets you rewrite the including class directly. module ClassMethods inside a Concern gets extended (copied) into the including class.
I would also like to explain the mechanism as it works. If I am not right please correct.
When we use include we are adding a linkage from our class to a module which contains some methods.
class A
include MyMOd
end
a = A.new
a.some_method
Objects don't have methods, only clases and modules do.
So when a receives mesage some_method it begin search method some_method in a's eigen class, then in A class and then in linked to A class modules if there are some (in reverse order, last included wins).
When we use extend we are adding linkage to a module in object's eigen class.
So if we use A.new.extend(MyMod) we are adding linkage to our module to A's instance eigen class or a' class.
And if we use A.extend(MyMod) we are adding linkage to A(object's, classes are also objects) eigenclass A'.
so method lookup path for a is as follows:
a => a' => linked modules to a' class => A.
also there is a prepend method which changes lookup path:
a => a' => prepended modulesto A => A => included module to A
sorry for my bad english.
I came across a very useful article that compares include, extend and prepend methods used inside a class:
include adds module methods as instance methods to the class, whereas extend adds module methods as class methods. The module being included or extended must be defined accordingly

Resources