Interfaces & Abstract Classes unnecessary in Ruby -- Can someone explain?
I am also Ruby starter. From my understanding, there is a closer rival for abstract classes in ruby. that is module
. you can't create any instances of module but you can include with another class. So a target class will get the whole functionality of parent
module Log
def write
//blah
end
end
class EventLog
include Log
def Prepare
end
end
In statically typed languages like java/C# , Interfaces enforce the classes to have all the methods at compile time. Since Ruby is dynamic, there is no meaning in it.For more clarity, check these posts why dynamic languages don't require interfaces..
- why-dont-we-require-interfaces-in-dynamic-languages
- why-do-dynamic-languages-like-ruby-and-python-not-have-the-concept-of-interfaces
Why do dynamic languages like Ruby and Python not have the concept of interfaces like in Java or C#?
Thanks to late binding, they do not need it. In Java/C#, interfaces are used to declare that some class has certain methods and it is checked during compile time; in Python, whether a method exists is checked during runtime.
Method overloading in Python does work:
>>> class A:
... def foo(self):
... return "A"
...
>>> class B(A):
... def foo(self):
... return "B"
...
>>> B().foo()
'B'
Are they object-oriented? I'd say yes. It's more of an approach thing rather than if any concrete language has feature X or feature Y. What is java interface equivalent in Ruby?
Ruby has Interfaces just like any other language.
Note that you have to be careful not to conflate the concept of the Interface, which is an abstract specification of the responsibilities, guarantees and protocols of a unit with the concept of the interface
which is a keyword in the Java, C# and VB.NET programming languages. In Ruby, we use the former all the time, but the latter simply doesn't exist.
It is very important to distinguish the two. What's important is the Interface, not the interface
. The interface
tells you pretty much nothing useful. Nothing demonstrates this better than the marker interfaces in Java, which are interfaces that have no members at all: just take a look at java.io.Serializable
and java.lang.Cloneable
; those two interface
s mean very different things, yet they have the exact same signature.
So, if two interface
s that mean different things, have the same signature, what exactly is the interface
even guaranteeing you?
Another good example:
package java.util;
interface List<E> implements Collection<E>, Iterable<E> {
void add(int index, E element)
throws UnsupportedOperationException, ClassCastException,
NullPointerException, IllegalArgumentException,
IndexOutOfBoundsException;
}
What is the Interface of java.util.List<E>.add
?- that the length of the collection does not decrease
- that all the items that were in the collection before are still there
- that
element
is in the collection
interface
? None! There is nothing in the interface
that says that the Add
method must even add at all, it might just as well remove an element from the collection.This is a perfectly valid implementation of that interface
:
class MyCollection<E> implements java.util.List<E> {
void add(int index, E element)
throws UnsupportedOperationException, ClassCastException,
NullPointerException, IllegalArgumentException,
IndexOutOfBoundsException {
remove(element);
}
}
Another example: where in java.util.Set<E>
does it actually say that it is, you know, a set? Nowhere! Or more precisely, in the documentation. In English.In pretty much all cases of interfaces
, both from Java and .NET, all the relevant information is actually in the docs, not in the types. So, if the types don't tell you anything interesting anyway, why keep them at all? Why not stick just to documentation? And that's exactly what Ruby does.
Note that there are other languages in which the Interface can actually be described in a meaningful way. However, those languages typically don't call the construct which describes the Interface "interface
", they call it type
. In a dependently-typed programming language, you can, for example, express the properties that a sort
function returns a collection of the same length as the original, that every element which is in the original is also in the sorted collection and that no bigger element appears before a smaller element.
So, in short: Ruby does not have an equivalent to a Java interface
. It does, however, have an equivalent to a Java Interface, and it's exactly the same as in Java: documentation.
Also, just like in Java, Acceptance Tests can be used to specify Interfaces as well.
In particular, in Ruby, the Interface of an object is determined by what it can do, not what class
it is, or what module
it mixes in. Any object that has a <<
method can be appended to. This is very useful in unit tests, where you can simply pass in an Array
or a String
instead of a more complicated Logger
, even though Array
and Logger
do not share an explicit interface
apart from the fact that they both have a method called <<
.
Another example is StringIO
, which implements the same Interface as IO
and thus a large portion of the Interface of File
, but without sharing any common ancestor besides Object
.
In Ruby, what is the equivalent to an interface in C#?
There are no interfaces in ruby since ruby is a dynamically typed language. Interfaces are basically used to make different classes interchangeable without breaking type safety. Your code can work with every Console as long it behaves like a console which in C# means implements IConsole. "duck typing" is a keyword you can use to catch up with the dynamic languages way of dealing with this kind of problem.
Further you can and should write unit tests to verify the behavior of your code. Every object has a respond_to?
method you can use in your assert.
Why don't we require interfaces in dynamic languages?
The interface
as a keyword and artifact was introduced by Java1 ( and C# took it from there ) to describe what the contract an object must adhere was.
But, interface has always been a key part of Object Oriented Paradigm and basically it represents the methods an object has to respond. Java just enforces this mechanism to provide statically type checking.
So, dynamic ( OO ) programming languages do use interfaces, even thought they don't statically check them. Just like other data types, for instance in Ruby:
@i = 1;
You don't have to declare i
of type FixNum
you just use it. Same goes for interfaces, they just flow. The trade-off is, you can't have a static check on that and failures are only show at runtime. In the other hand Structural type ( or static duck type as I call it :P ) used by languages as Go or Scala, gives the best of both worlds.
1. See Daniel Earwicker comment about CORBA interface
keyword
Why Ruby doesn't support abstract classes?
If you are considering abstract classes or interfaces, you are most likely thinking of creating some kind of contract to your code.
However, Ruby is designed to be a weakly-typed is not designed to be a statically typed language and does rely heavily on duck typing. Obviously, it might be really useful in some cases to perform an interface check (to ensure the passed object would support all needed methods, for instance), but it is still will be done at run time, rendering the feature practically useless.
As far as I remember, there was an intention to create a typed version or Ruby and Dave Thomas even mentioned a person who tried this and told that it is not working out well :)
Why is the OO concept interface not represented by a keyword in C++?
The early OO features of C++ have long been neglected because it has since moved in a more interesting direction as a multi-paradigm language. The major focus for over a decade now has been templates and their implications, particularly in the standard library. Yes, programs would be more readable with an interface
keyword. They would also be easier to maintain if there were override
and new
modifiers for methods that have the same name as base class methods (a la C#). But these are not interesting problems to modern C++ users, nor to those who contribute to the language design. The OO features are adequate, but not great, and are hardly used in the "newer" (post 1992) parts of the standard library, which in some ways serves as a guide to good style.
What is the point of interfaces in a weakly-typed language like PHP?
Interfaces cause your program to fail earlier and more predictably when a subclass "forgets" to implement some abstract method in its parent class.
In PHP's traditional OOP, you had to rely on something like the following to issue a run-time error:
class Base_interface {
function implement_me() { assert(false); }
}
class Child extends Base_interface {
}
With an interface, you get immediate feedback when one of your interface's subclasses doesn't implement such a method, at the time the subclass is declared rather than later during its use. Minimal interface for Range#include? support
The documentation is incorrect or (rather, I suspect) outdated. Range#cover?
works the way you expect [bold emphasis mine]:
The documentation forReturns
cover?(object)
→true
orfalse
true
if the given argument is withinself
,false
otherwise.With non-range argument object, evaluates with
<=
and<
.
Range#include?
contains a somewhat ominous statement [bold emphasis mine]:If begin and end are numeric,Here you can see the difference:include?
behaves likecover?
[…]
But when not numeric, the two methods may differ:
('a'..'d').include?('cc') # => false
('a'..'d').cover?('cc') # => true
Range#cover?
evaluates to true
because 'a' <= 'cc' && 'cc' <= 'd'
, whereas Range#include?
evaluates to false
because ('a'..'d').to_a == ['a', 'b', 'c', 'd']
and thus ('a'..'d').each.include?('cc')
is falsey.Note that the introductory example using Time
still works because Time
is explicitly special-cased in the spec.
There is a spec which says both Range#include?
and Range#cover?
use <=>
, but it is only tested with Integer
s, for which we know from the ominous documentation above that Range#include?
and Range#cover?
behave the same.
There is quite a lot of special-casing going on for Range
s and it is not the first time this has led to bugs and/or non-intuitive behavior:
- Ruby: Can't Iterate From Time Despite Responding to Succ / Bug #18237 Remove unnecessary checks for
Time
inRange#each
as per the comment / https://github.com/ruby/spec/pull/852 / https://github.com/ruby/ruby/pull/4928 - https://bugs.ruby-lang.org/issues/18155
- https://bugs.ruby-lang.org/issues/18577
- https://bugs.ruby-lang.org/issues/18580
- https://github.com/ruby/dev-meeting-log/blob/master/DevMeeting-2022-02-17.md#bug-18580-rangeinclude-inconsistency-for-beginless-string-ranges-zverok
I would file a bug, if only to get some clarification into the docs and specs.
Related Topics
How to "Break" Out of a Case...While in Ruby
How to Test Strong Params with Rspec
Filtering Sensitive Data with Vcr
How to Downgrade My Rails Version
Activestorage Issue to Set Record_Type on Active_Storage_Attachments
Difference Between "Class A; Class B" and "Class A::B"
Can't Install Nokogiri for Ruby in Windows
Resizing an Image with Mini_Magick
How to Convert a Fraction to Float in Ruby
How to Set in a Middleware a Variable Accessible in All My Application
How to Scrape a Website with The Socksify Gem (Proxy)
Without Converting to a String, How Many Digits Does a Fixnum Have
How to Access Sinatra App on Host Machine with Vagrant Forwarded Ports