The Ruby programming language has very elegantly, and in a clever manner, weaved together a number of well-known, and well-understood programming paradigms. We list below some of these paradigms. In a series of following posts, we highlight some of the ruby language features as viewed within the context of these paradigms.
1. Imperative Paradigm:
Statements: The program is a sequence of statements, where each statement operates on the current state of the program (represented by variables), and transforms the state. Statements include assignments, control flow (conditionals, loops), and function/procedure calls.
Expressions: Values, variables, and functions connected using operators and operator precedence rules.
Assignments: The value of an expression is assigned to a variable. The assignment operator is typically either “=” or “:=”.
Control Flow: Includes conditionals (if-then-else, case) and loops (while, for).
Procedures: A construct to structure the statements of a program. The statements within the procedure are executed by calling the procedure with one or more parameters, and returning one or more values. Procedures can be with and without side-effects.
Scope: Determines the visibility and accessibility of variables during the execution of the program.
2. Dynamic Typing:
Type concept: The type of a value/variable constrains the values and operations on the value/variable.
Static Typing: The values which a variable can hold are constrained by the type of that variable through a type declaration. The language implementation enforces the type constraints, and these constraints can be determined at compile time.
Dynamic Typing: Values have types. Variables do not have any type declarations and the variable can hold objects of different types. Type checking occurs at runtime.
Strong and Weak Typing: In a strongly typed language, almost all operations are checked for type correctness. In a weakly typed language the type checking is less strict, and usually supports some form of type conversion and overloading.
Method (or Dynamic) Dispatch: Method invocations are viewed as messages sent to an object. The actual method to invoke is determined at runtime (also referred to as dynamic binding).
Duck Typing: Type of an object is determined by what it can do, and not by it’s class. A statement calling a method “m” on an object does not rely on the declared type of the object; only that the object, of whatever type, must implement the method “m”.
3. Object-Oriented Paradigm:
Class: A class defines a structure and common behavior of a group of instances. In other words, a class is a template for it’s instances. A class has data definitions (class and instance variable) and method definitions (class and instance methods). In some languages (e.g., Ruby, Python), classes are also objects.
Object: An instance of a class.
Metaclass: A metaclass is a class whose instances are classes. Just as an object is an instance of its class, a class is an instance of its metaclass.
Class Inheritance: A sub-class can inherit properties from a superclass.
Mixin: A mixin or module is a collection of methods and instance variables. Mixins are not instantiable. Typically, mixins will not have instance variables. A mixin that requires its own state should be written as a class. Class can include one or more mixins. Mixins get inserted into the class hierarchy just above the including class. Since a mixin acts like a sort of super class, a mixin cannot override a method in the class that includes it. Mixin allows for code-reuse, without some of the problems of multiple inheritance
4. Functional Paradigm:
Functions: Function (y = f(x)) takes inputs, and returns output. Functions don’t change state, and there are no side-effects (do not modify input parameters, do not modify global variables). A function called again with the same inputs, will return the same output. Reuse code by function composition (eg: tan(x) = sin(x) / cos(x)).
First-class objects: An entity that can be assigned into a variable, constructed at runtime (dynamic creation), passed as a parameter, and returned from a subroutine. For example, scalar datatypes such as integer, float, string, symbol, array, hash are first class objects. In functional programming languages, functions are first-class objects. In some languages, classes can also be first-class objects.
Closures: First class functions which can close over variables in their surrounding environment at creation time. Closure = <function, environment>.
Map: Takes a sequence/collection, transforms each item in the sequence with a conversion function, returns another sequence.
Filter: Takes a sequence/collection, returns another sequence which is filtered by a predicate.
Reduce: Takes a sequence/collection, iterate through each item in the sequence, and build up a result value.