class DOTTORANDO inherit STUDENTE DOCENTE …
class PERSONS inherit DICTIONARY[NAME,PERSON->COMPARABLE]…
?:= ::=
, 32-64-128-bits floats NONE
class EULERO -- Project Euler Question 1
create {ANY} make
feature {ANY}
low: INTEGER 1; high: INTEGER 999
make do
io.put_string("Sum of all naturals divisible by 3 or 5 lower than 1000 is: ")
(low |..| high).aggregate(
agent (progressive, i: INTEGER): INTEGER
do
if i.divisible(3) or i.divisible(5) then Result := progressive + i
else Result := progressive
end
end (?, ?), 0).print_on(std_output)
end
end -- class EULERO
Ovvero della formalizzazione dei contratti.
deferred class INDEXABLE [E_]
…
item (i: INTEGER): E_
-- Item at the corresponding index `i'.
require valid_index(i)
deferred
end
deferred class COLLECTION [E_]
inherit STORABLE… TRAVERSABLE[E_]… SEARCHABLE[E_]…
frozen infix "@" (i: INTEGER): E_
-- The infix notation, just a synonym for `item'.
require valid_index(i)
do Result := item(i)
ensure definition: Result = item(i)
end
feature add_last (element: E_) -- Add a new item at the end.
deferred
ensure
last = element
count = 1 + old count
lower = old lower
upper = 1 + old upper
Le precondizioni possono solo essere allentate, meno obblighi per il chiamante.
Le postcondizioni posono solo essere rinforzate, più garanzie per il cliente.
Vediamo un esempio "al negativo"
class PARENT
feature negate(x: INTEGER): INTEGER
require positive: x>0
class HEIR
inherit PARENT
redefine negate
feature negate (x: INTEGER): INTEGER
require big: x>100
class MY_PROGRAM
…
feature p: PARENT; h: HEIR
@startuml
abstract class ABSTRACT_HASHED_DICTIONARY
abstract class ABSTRACT_STRING {
infix "+"
infix "|"
infix "&"
infix "#"
infix "^"
}
abstract class ARRAYED_COLLECTION
abstract class COLLECTION {
infix "@"
put
has
}
abstract class COMPARABLE {
infix "<"
infix ">"
compare
in_range
}
abstract class HASHABLE {
hash_code
}
abstract class HOARD {
count
is_empty
}
abstract class INDEXABLE {
item
lower
upper
}
abstract class ITERABLE {
new_iterator
for_each
for_all
exists
aggregate
}
abstract class MAP {
has
at
}
abstract class NATIVELY_STORED_STRING
abstract class PARTIALLY_FILLED_STRING
abstract class SEARCHABLE {
has
index_of
}
abstract class TRAVERSABLE {
enumerate
}
abstract class ABSTRACT_AVL_DICTIONARY
class AVL_DICTIONARY extend COMPARABLE>
abstract class ABSTRACT_PYTHON_DICTIONARY
abstract class AVL_TREE
class ARRAY
abstract class ARRAYED_DICTIONARY
abstract class DICTIONARY {
at
put
remove
}
class FAST_ARRAY
class FIXED_STRING
class HASHED_DICTIONARY extend HASHABLE>
class LAZY_STRING
abstract class LINKED_COLLECTION
class LINKED_LIST
class PYTHON_DICTIONARY extend HASHABLE>
class QUEUE
class RING_ARRAY
class ROPE
abstract class SIMPLE_DICTIONARY
abstract class STORABLE
class STRING {
put
}
class TWO_WAY_LINKED_LIST
ABSTRACT_AVL_DICTIONARY --|> SIMPLE_DICTIONARY
ABSTRACT_AVL_DICTIONARY ..|> AVL_TREE
ABSTRACT_HASHED_DICTIONARY --|> SIMPLE_DICTIONARY
ABSTRACT_PYTHON_DICTIONARY --|> SIMPLE_DICTIONARY
ABSTRACT_STRING --|> COMPARABLE
ABSTRACT_STRING --|> HASHABLE
ABSTRACT_STRING --|> SEARCHABLE
ABSTRACT_STRING --|> STORABLE
ABSTRACT_STRING --|> TRAVERSABLE
AVL_DICTIONARY --|> ABSTRACT_AVL_DICTIONARY
ARRAY --|> COLLECTION
ARRAY --|> ARRAYED_COLLECTION
FAST_ARRAY --|> COLLECTION
FAST_ARRAY --|> ARRAYED_COLLECTION
ARRAYED_COLLECTION --|> COLLECTION
ARRAYED_DICTIONARY --|> DICTIONARY
COLLECTION --|> SEARCHABLE
COLLECTION --|> STORABLE
COLLECTION --|> TRAVERSABLE
DICTIONARY --|> MAP
FIXED_STRING --|> NATIVELY_STORED_STRING
HASHED_DICTIONARY --|> ABSTRACT_HASHED_DICTIONARY
INDEXABLE --|> HOARD
ITERABLE --|> HOARD
LAZY_STRING --|> ABSTRACT_STRING
LINKED_COLLECTION --|> COLLECTION
LINKED_LIST --|> COLLECTION
LINKED_LIST ..|> LINKED_COLLECTION
MAP --|> TRAVERSABLE
NATIVELY_STORED_STRING --|> ABSTRACT_STRING
PARTIALLY_FILLED_STRING --|> ABSTRACT_STRING
PYTHON_DICTIONARY --|> ABSTRACT_PYTHON_DICTIONARY
QUEUE ..|> RING_ARRAY
RING_ARRAY --|> COLLECTION
RING_ARRAY ..|> ARRAYED_COLLECTION
ROPE --|> ABSTRACT_STRING
SEARCHABLE --|> INDEXABLE
SIMPLE_DICTIONARY --|> DICTIONARY
STRING --|> NATIVELY_STORED_STRING
TRAVERSABLE --|> INDEXABLE
TRAVERSABLE --|> ITERABLE
TWO_WAY_LINKED_LIST --|> COLLECTION
TWO_WAY_LINKED_LIST ..|> LINKED_COLLECTION
@enduml
qui per ingrandire
class PERSON
feature spouse: PERSON
invariant
(spouse /= Void) implies (spouse.spouse = Current)
Vale anche per "germano" che significa "fratello o sorella".
Il classico Fibonacci, ma con un ciclo
fibonacci_loop (n: INTEGER): like n
require n>=0
local i,p,q: like n
do
inspect n
when 0 then Result := 0
when 1 then Result := 1
else
from i:=2; p:=0; q:=1; Result:=1
invariant Result > 0
variant n-i -- shall decrease
until i>=n
loop
p := q; q := Result
Result := p + q; i := i+1
end
end
end
ensure no_side_effects: Current.is_equal(old Current)
Il perimetro dell'ellisse (vedi ELLIPSE negli esempi
class ELLIPSE
inherit ANY
insert MATH_CONSTANTS export {} all end
create{ANY}
with_axes,
with_foci_semidistance_and_eccentricity
feature {ANY}
perimeter: REAL
-- Implemented using a Ramanujan's approximation https://en.wikipedia.org/wiki/Perimeter_of_an_ellipse
do
Result := Pi * (3 * (a+b) - ((3*a+b)*(a+3*b)).sqrt)
end
a: REAL -- major semiaxis
b: REAL -- minor semiaxis
feature {ANY}
set_a (a_value: REAL) assign a do a:=a_value end
set_b (a_value: REAL) assign b do b:=a_value end
feature {ANY}
with_axes (major,minor: like a)
require major>minor
do
a := major/2
b := minor/2
end
with_foci_semidistance_and_eccentricity (c, e: REAL)
require
meaningful_foci_semidistnace: c>0
meaningful_eccentricity: e>0
do
a := c/e
b := (a^2-c^2).sqrt
end
end
class ELLIPSE ...
feature {ANY}
perimeter: REAL do
Result := Pi * (3 * (a+b) - ((3*a+b)*(a+3*b)).sqrt)
end
...
end
class ELLIPSE2
inherit ELLIPSE redefine with_axes, perimeter
create{ANY} with_axes
feature {ANY}
perimeter: REAL attribute
with_axes (major,minor: like a)
do
Precursor(major, minor)
perimeter := 2 * Pi * ((a^2+b^2)/2).sqrt
end
end
class UNIFORM_ACCESS_PRINCIPLE create {} compute_perimeter
feature {ANY} compute_perimeter do
create ellipse.with_axes(5.0, 3.0)
("Perimeter of ellipse with semi-axes #(1) and #(2) equals #(3)%N"
# ellipse.a.out # ellipse.b.out
# ellipse.perimeter.out).print_on(std_output)
ellipse.b := 2.0
create {ELLIPSE2} another.with_axes(5.0, 3.0)
("Crudely precomputed perimeter of another ellipse with same semi-axes is #(1)%N"
# another.perimeter.out).print_on(std_output)
end
ellipse: ELLIPSE; another: ELLIPSE
end
public class Foo {
private string _name;
public int Size { // Property
get; // Getter
set; // Setter
}
public string Name { // Property
get { return _name; } // Getter
set { _name = value; } // Setter
}
}
```
class FOO
feature {ANY}
name: STRING
size: INTEGER
feature {PIPPO}
set_name (a_value: STRING) assign name
do name:=a_value end
feature {BAR}
set_size (a_value: like size) assign size
require a_value > 12
do size:=a_value end
end
Gli argomenti, solo quelli necessari … E le opzioni? Sono comandi separati! Ovvero non
operazione (arg1, arg2, option="->", another_option=23)
Bensì:
set_option("->"); set_another_option(23)
operazione (arg1,arg2)
O meglio ancora (usando assign
)
option:="->"; another_option:=23; be_verbose; operazione(arg1, arg2)
Sì, è possibile TODO: finire
class DOTTORANDO
DOTTORANDO --|> DOCENTE
DOTTORANDO --|> STUDENTE
DOCENTE --|> PERSON
STUDENTE --|> PERSON
class STUDENTE inherit PERSON … end
class DOCENTE inherit PERSON … end
class DOTTORANDO
inherit
STUDENTE
rename
export
undefine
redefine
DOCENTE …
public, private, friend non bastano
class EFFECTIVE_ARG_LIST_0 -- An empty effective argument list (for a command, aka routine call).
…
feature {ANY}
…
end_position: POSITION
…
feature {EIFFEL_PARSER, EFFECTIVE_ARG_LIST}
set_end_position (p: POSITION) assign end_position
do
end_position := p
end
and then, or else, implies: operatori logici semistrict, evaluation stops when the result is known.
has_value (v: V_): BOOLEAN
-- Is there a value `v'?
require v /= Void
deferred
ensure
Result implies has(key_at(v))
Result = (occurrences(v) = 1)
end
Da BIJECTIVE_DICTIONARY
a | b | a implies b |
---|---|---|
✔ | ✔ | ✔ |
✔ | ✘ | ✘ |
✘ | ✔ | ✔ |
✘ | ✘ | ✔ |
feature
apply_interests
deferred
ensure interest>0 implies capital > old capital
feature
deferred class DICTIONARY [A_VALUE, A_KEY]
....
add (a_value: A_VALUE; a_key: A_KEY)
-- Adds 'a_value' with 'a_key' to Current DICTIONARY.
require not has(a_key)
deferred
ensure count = 1 + old count; v = at(a_key)
end
"True Eiffel" resembles "pseudo code in English": inline agents are closures
repeat (i: INTEGER; n: STRING): like n
require i>0; n/=Void
local repeated: like n
do
repeated := ""
i.times(agent do repeated.append(n) end)
Result := repeated
end
deferred class INTEGER_GENERAL
feature {ANY}
infix "+" (other: like Current): like Current
external "built_in"
end
infix "-" (other: like Current): like Current
external "built_in"
end
multiply alias "*" (other: like Current): like Current
external "built_in"
end
"zucchero sintattico"
class FUNZIONE -- A function-like object
create default_create
feature
compute alias "()" (i,j: INTEGER): INTEGER do
Result := i * j
std_output.put_line("i:=#(1), j:=#(2) → result #(3)" # &i # &j # &Result)
end
end
foo (f: FUNZIONE) do
print (f(12,33).out)
end
class MATRIX
-- A simplicistic matrix with C-like setters and getters
inherit FAST_ARRAY2[REAL]
create make
feature
item_at alias "[]" (i,j: INTEGER): like item do
Result := item(i,j)
end
assigner (x: REAL; i,j: INTEGER) assign item_at do
put(x,i,j)
end
end
class PARENTESES
-- An example on how to use "[]" and "()" operators
create make
feature
m: MATRIX; f: FUNZIONE
make do
create m.make(6,6)
create f
m[2,2] := 12.3
print("m[2,2] = " + (m[2,2].out)+"%N")
print("f is an object but f(3,7) = "+f(3,7).out+"%N")
end
end -- class PARENTESES
class INTEGER_32
…
infix "|..|" (other: INTEGER): INTEGER_RANGE[INTEGER]
require Current <= other
do
create Result.make(Current, other, integer_range_noop, integer_range_noop)
end
Inizia e finisce con uno dei +-*/\=<>@#|&~
The best optimization is a good design
Solo un metodo di documentazione? O piuttosto quasi un linguaggio di specifica? Di certo guida l'implementazione
STRING
a!SmallEiffel "+"… e finisci con un O(n²)!
class STRING
abstract class HASHABLE
abstract class COMPARABLE
STRING --|> HASHABLE
STRING --|> COMPARABLE
SmartEiffel 2.3
class STRING
abstract class HASHABLE
abstract class COMPARABLE
abstract class STORABLE
abstract class TRAVERSABLE
STRING --|> HASHABLE
STRING --|> COMPARABLE
STRING --|> STORABLE
STRING --|> TRAVERSABLE
LibertyEiffel le precondizioni e le postcondizioni hanno guidato l'implementazione di ROPE e delle altre
abstract class ABSTRACT_STRING {
infix "+"
infix "|"
infix "&"
infix "#"
infix "^"
}
abstract class COMPARABLE {
infix "<"
infix ">"
compare
in_range
}
abstract class HASHABLE {
hash_code
}
abstract class HOARD {
count
is_empty
}
abstract class INDEXABLE {
item
lower
upper
}
abstract class ITERABLE {
new_iterator
for_each
for_all
exists
aggregate
}
abstract class NATIVELY_STORED_STRING
abstract class PARTIALLY_FILLED_STRING
abstract class TRAVERSABLE {
enumerate
}
ABSTRACT_STRING --|> COMPARABLE
ABSTRACT_STRING --|> HASHABLE
ABSTRACT_STRING --|> SEARCHABLE
ABSTRACT_STRING --|> STORABLE
ABSTRACT_STRING --|> TRAVERSABLE
FIXED_STRING --|> NATIVELY_STORED_STRING
INDEXABLE --|> HOARD
ITERABLE --|> HOARD
LAZY_STRING --|> ABSTRACT_STRING
NATIVELY_STORED_STRING --|> ABSTRACT_STRING
PARTIALLY_FILLED_STRING --|> ABSTRACT_STRING
ROPE --|> ABSTRACT_STRING
SEARCHABLE --|> INDEXABLE
STRING --|> NATIVELY_STORED_STRING
TRAVERSABLE --|> INDEXABLE
TRAVERSABLE --|> ITERABLE
@startuml
abstract class ABSTRACT_STRING {
infix "+"
infix "|"
infix "&"
infix "#"
infix "^"
}
abstract class COMPARABLE {
infix "<"
infix ">"
compare
in_range
}
abstract class HASHABLE {
hash_code
}
abstract class HOARD {
count
is_empty
}
abstract class INDEXABLE {
item
lower
upper
}
abstract class ITERABLE {
new_iterator
for_each
for_all
exists
aggregate
}
abstract class NATIVELY_STORED_STRING {
storage,
count
}
abstract class PARTIALLY_FILLED_STRING
abstract class SEARCHABLE {
has
index_of
}
abstract class TRAVERSABLE {
enumerate
}
class FIXED_STRING {
substring,
}
class LAZY_STRING
class ROPE
abstract class STORABLE
class STRING {
resize,
append,
prepend,
insert_string,
replace_substring,
put, ...
}
ABSTRACT_STRING --|> COMPARABLE
ABSTRACT_STRING --|> HASHABLE
ABSTRACT_STRING --|> SEARCHABLE
ABSTRACT_STRING --|> STORABLE
ABSTRACT_STRING --|> TRAVERSABLE
FIXED_STRING --|> NATIVELY_STORED_STRING
INDEXABLE --|> HOARD
ITERABLE --|> HOARD
LAZY_STRING --|> ABSTRACT_STRING
NATIVELY_STORED_STRING --|> ABSTRACT_STRING
PARTIALLY_FILLED_STRING --|> ABSTRACT_STRING
ROPE --|> ABSTRACT_STRING
SEARCHABLE --|> INDEXABLE
STRING --|> NATIVELY_STORED_STRING
TRAVERSABLE --|> INDEXABLE
TRAVERSABLE --|> ITERABLE
@enduml
Mio caro Frodo. … perché per quanto
io ti abbia
raccontato la verità, magari non era tutta tutta
Liberty implementa Eiffel, magari non lo implementa tutto
tutto…
Null References: The Billion Dollar MistakeTony Hoare
Fiumi di require foo/=Void
compute (a_foo: FOO) do
if a_foo attached as f then
-- f is garanteed to be non-Void
else
compute_without_foo
end
compute (a_foo: attached FOO)
class INTEGER_32
convert to_natural
feature
to_natural: NATURAL_32 -- aka unsigned int!
che è più di multithreading o di fork
E perché non permettere cose come ≔ ≅ ⊂ ⊃ a² a³
Non è il COBOL. Anche l'estrema concisione è pericolosa
$ node
Welcome to Node.js v20.19.0.
Type ".help" for more information.
> (() => {})();
undefined
> ((() => {}))();
undefined
> (() => ({}))();
{}
(agent do end).call -- "empty command"
(agent do end).call -- "empty command in a parentesis"
(agent: ANY do end).item() -- "a Void reference to ANY object, returned by a closure"
Lo pseudo-codice lo impaginate col Times New Roman!
La sintassi di Eiffel è pensata esplicitamente per sembrare pseudo-codice...
Quindi largo al Serif!
Se Eiffel è così buono perché non si è ovunque?
The Secret of a Successful Programming Language? A Really Great Beard
… Bertrand Meyer non porta la barba!
Libri liberamente accessibili di Bertrand Meyer
paolo@monodes.com
paolo.redaelli@gmail.com
paolo.redaelli@polimi.it