Skip to content

Commit

Permalink
Merge pull request #26 from dullin/refactor-core-concepts
Browse files Browse the repository at this point in the history
Refactor object-oriented principles into a single page
  • Loading branch information
privat authored Jul 19, 2024
2 parents 24f86ad + e39ca1f commit d91de85
Show file tree
Hide file tree
Showing 9 changed files with 187 additions and 180 deletions.
28 changes: 0 additions & 28 deletions pages/covariance.md

This file was deleted.

32 changes: 0 additions & 32 deletions pages/everything_is_an_object.md

This file was deleted.

8 changes: 4 additions & 4 deletions pages/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ Nit's goal is to be usable by real programmers for real projects

Nit's guideline is to follow the most powerful OO principles

* [[Everything is an object]]
* [[Multiple inheritance]]
* [[Static open classes|refinement]]
* [[Virtual types]]
* [[Everything is an object|object_oriented_principles#Everything_is_an_Object]]
* [[Multiple inheritance|object_oriented_principles#Multiple_Inheritance]]
* [[Static open classes|object_oriented_principles#Class_Refinement]]
* [[Virtual types|object_oriented_principles#Virtual_Types]]

## Robustness

Expand Down
15 changes: 0 additions & 15 deletions pages/module.md

This file was deleted.

18 changes: 0 additions & 18 deletions pages/multiple_inheritance.md

This file was deleted.

182 changes: 182 additions & 0 deletions pages/object_oriented_principles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
# Everything is an Object

In Nit, all data, even primitive stuff like integers and booleans, are objects.
It means :

### They are subtype of Object

~~~
fun foo(o: Object) do print "I am {o}, the {o.class_name}"
foo(4) # -> I am 4, the Int
foo(true) # -> I am true, the Bool
~~~

### Their classes can be [[refined|#Class_Refinement]]

~~~
redef class Int
fun double: Int do return self * 2
end
print 12.double # -> 24
~~~

### And it is real

It is not just some automatic boxing (a la Java 1.5).

~~~
var a: Object = 5
var b: Object = 3 + 2
print a == b # -> true
print a.is_same_instance(b) # -> true
~~~

# Module

In Nit, a program consists of a collection of modules.
A module in Nit serves the following purposes:

* grouping classes and methods related to a single concern
* controlling visibility and name-spaces
* controlling [[refinement|#Class_Refinement]] of classes

Modules are connected by importation links (with the keyword `import`) and a Nit program is simply a module that imports all the other modules.

Note that the importation between modules cannot be circular.

By default, the module `standard` is imported by every modules.
It contains some basic classes and methods usually expected by the developers.


# Multiple Inheritance

You hate copy-pasting? you like polymorphism? you like static-typing? Then multiple inheritance is for you.

In Nit, all kind of classes (including interfaces) can provide methods with bodies.
All kind of classes (including concrete classes) can specialize one or more super-class.

### But multiple inheritance is too complex for my mind, isn't it?

In Nit, inheritance (and especially multiple inheritance) is done right:

* No repeated inheritance: inheritance is only based on a principle of specialization. Therefore, a class is either inherited or is not inherited. A Cow is an Animal, there is no point to even ask "how many time a Cow is an Animal?"

* No explicit transitive specialization. If a class already specializes an other class by transitivity, explicitly declaring this class as a super-class has no effect (and a compiler might gives you a warning)

* No hidden inheritance: specialization is public, therefore if you can see the classes, you can see their specialization links.

* No static overloading: A method or an attribute introduced in a super-class is always inherited and cannot be mistaken with something different.

# Class Refinement

Class refinement is the Nit way to do open classes.
The principle is basic: a [[module|#Module]] can refine a class to improve it (the correct world might be "extend" but it could be misinterpreted because of Java).

To refine a class, use the keyword `redef`.

redef class String
fun ellipsis(length: Int): String
do
if self.length <= length then return self
return self.substring(0, length) + "..."
end
end

print "hello world!".ellipsis(4) # -> "hell..."

Note that all classes can be refined, even primitive ones because [[everything is an object|#Everything_is_an_Object]].

Once a class is refined, all its instances and all its sub-classes gains the refinement.

Refinement can also be used to:

* redefine an existing method
* add attributes
* add new super-classes

### When should I do refinement and when should I do specialization?

It is a tricky question since the best answer depends on the concrete usecase and problem.
However, there is a good rule of thumb: If you hesitate between refinement and specialization, then it is specialization.

# Virtual Types

Virtual type is a way to define in a class a property that is associated with a type.
This property can be used to type parameters, return of functions, variables, etc.
Virtual types are inherited by subclasses and can be redefined.

Virtual types is a good solution when:

* A class need to refer to the real itself
* Two class hierarchy are somewhat parallel
* You want [[covariance|#Covariant_Typing_Policy]]

### Usage

Example, we have employees what works in standard office and bosses, that are technically employees, but hat works in boss offices (that are improved offices with a fridge).

One way to achieve the modeling is to use virtual types.
virtual types are defined inside class (like methods and attributes) but with the keyword `type`.
Inside the class, the virtual type can be used (almost) like any other static type.

~~~
class Employee
type OFFICE: Office
var office: OFFICE
end
class Office
# ...
end
~~~

Subclasses can redefine (with `redef`) the virtual types they want to change.
The only requirement is that the new bound is a sub-type of the previous bound.

~~~
class Boss
super Employee
redef type OFFICE: BossOffice
end
class BossOffice
super Office
end
~~~

The redefinition of a virtual type is used by the compiler to prevent some type errors.
For example, assuming that boss' offices have a fridge:

~~~nitish
var e: Employee = ...
e.office.fridge.open # Compilation Error! Office has no method fridge
var b: Boss = ...
b.office.fridge.open # OK!
~~~

# Covariant Typing Policy

Covariance in Nit has two causes:

* generic types are covariant on the base type: ie `Array[Cat]` is a subtype of `Array[Object]` because `Cat` is a subtype of `Object`. This is not true in most of object-oriented languages.
* [[virtual types|#Virtual_Types]] can be redefined

### Advantages and drawbacks of the covariance

The main advantage of the covariance is the improved expressiveness to the Nit models.

~~~
class Employee
type OFFICE: Office
var office: OFFICE
end
class Boss
super Employee
redef type OFFICE: BossOffice
end
var b: Boss = ...
var o: Office = ...
b.office = o # Compilation Error! Expected a BossOffice
~~~

Invariant languages (eg. Java, C++) have to use unsafe downcasts to achieve the same effects.
31 changes: 0 additions & 31 deletions pages/refinement.md

This file was deleted.

52 changes: 0 additions & 52 deletions pages/virtual_types.md

This file was deleted.

1 change: 1 addition & 0 deletions sidebar/documentation.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
### [[Documentation|doc]]

* [[Object-oriented principles|object_oriented_principles]]
* [[Language manual|manual]]
* [[Compiler and tools|tools]]
* [Standard library](/stdlib)
Expand Down

0 comments on commit d91de85

Please sign in to comment.