Difference between revisions of "EiffelBase2"

(Iterators)
 
(17 intermediate revisions by the same user not shown)
Line 7: Line 7:
 
==Download==
 
==Download==
  
The latest version of the EiffelBase2 source code is available in the repository: [https://svn.origo.ethz.ch/eiffelbase2/trunk/ https://svn.origo.ethz.ch/eiffelbase2/trunk/]
+
The latest version of the EiffelBase2 source code is available from the repository: [https://bitbucket.org/nadiapolikarpova/eiffelbase2/ https://bitbucket.org/nadiapolikarpova/eiffelbase2/]
 +
 
 +
The source code is divided into two clusters:
 +
 
 +
* <e>structures</e> - Data Structures, the core of EiffelBase2. The rest of current document is about this cluster.
 +
 
 +
* <e>mml</e> - Mathematical Model Library: a library of immutable classes used in ''model-based contracts'' (see below).
  
 
==Goals==
 
==Goals==
Line 20: Line 26:
  
 
==Design==
 
==Design==
On the top level EiffelBase2 differentiates between ''containers'' and interfaces to access elements of containers, called ''accessors''. A container is a finite storage of values. Accessors are either ''maps'' (accessing elements by a unique key) or ''streams'' (linear access). An observer is not necessarily bound to a container, e.g. <e>RANDOM</e> stream observes an infinite sequence of pseudo-random numbers. Streams that traverse containers are called ''iterators''.
+
On the top level EiffelBase2 differentiates between ''containers'' and ''streams''. A container is a finite storage of values, while a stream provides linear access to a set of values. A stream is not necessarily bound to a container, e.g. <e>RANDOM</e> stream observes an infinite sequence of pseudo-random numbers. Streams that traverse containers are called ''iterators''.
  
Below you can find the class diagram of EiffelBase2, split into two hierarchies. The first one is a hierarchy of containers and maps. All EiffelBase2 class names start with <e>V_</e> (for ''Verified''), but the prefix is omitted in the current document for brevity.  
+
Below you can find the class diagram of EiffelBase2, split into two hierarchies: containers and streams/iterators.  
[[Image:eb2_container.png|800px|thumb|none|Container class hierarchy]]
+
All EiffelBase2 class names start with <e>V_</e> (for ''Verified''), but the prefix is omitted in the current document for brevity.
  
The second one is a hierarchy of streams and iterators.
+
In the diagrams below asterisk and italics font indicates a <e>deferred</e> class.
[[Image:eb2_iterator.png|800px|thumb|none|Iterator class hierarchy]]
+
Lighter fill color indicates that the class provides an ''immutable'' interface to the data,
 +
in other words, it is impossible to modify the content of the container using this interface.
 +
 +
[[Image:eb2_container.png|1000px|thumb|none|Container class hierarchy]]
 +
 
 +
[[Image:eb2_iterator.png|1000px|thumb|none|Iterator class hierarchy]]
  
 
==Usage examples==
 
==Usage examples==
 +
 +
===Immutable interfaces===
 +
With immutable interfaces you can give your clients read-only access to a container you store:
 +
<e>
 +
class TRAM_LINE
 +
 +
feature -- Access
 +
   
 +
    stations: V_SEQUENCE [STATION]
 +
            -- Stations on the line.
 +
            -- (Clients can traverse the stations, but cannot replace them, remove or add new ones.)
 +
        do
 +
            Result := station_list
 +
        end
 +
 +
feature {NONE} -- Implementation
 +
 +
    station_list: V_LIST [STATION]
 +
            -- List of line stations.
 +
end
 +
</e>
  
 
===Iterators===
 
===Iterators===
 
Here is how you can iterate through any container:
 
Here is how you can iterate through any container:
 +
<e>
 +
do_something (container: V_CONTAINER [INTEGER])
 +
    do
 +
        across
 +
            container as i
 +
        loop
 +
            print (i.item)           
 +
        end
 +
    end
 +
</e>
 +
 +
The same thing using the explicit syntax:
 
<e>
 
<e>
 
do_something (container: V_CONTAINER [INTEGER])
 
do_something (container: V_CONTAINER [INTEGER])
 
     local
 
     local
         i: V_INPUT_ITERATOR [INTEGER]
+
         i: V_ITERATOR [INTEGER]
 
     do
 
     do
 
         from
 
         from
             i := container.new_iterator
+
             i := container.new_cursor
 
         until
 
         until
 
             i.after
 
             i.after
Line 66: Line 110:
 
===Sets and tables===
 
===Sets and tables===
  
Here is how you create and use a simple has table (keys must inherit from HASHABLE):
+
Here is how you create and use a simple hash table (keys must inherit from HASHABLE):
 
<e>
 
<e>
 
do_something
 
do_something
Line 99: Line 143:
  
 
Similar style applies to <e>V_HASH_SET</e> and <e>V_GENERAL_HASH_SET</e>, <e>V_SORTED_TABLE</e> and <e>V_GENERAL_SORTED_TABLE</e>, <e>V_SORTED_SET</e> and <e>V_GENERAL_SORTED_SET</e>.
 
Similar style applies to <e>V_HASH_SET</e> and <e>V_GENERAL_HASH_SET</e>, <e>V_SORTED_TABLE</e> and <e>V_GENERAL_SORTED_TABLE</e>, <e>V_SORTED_SET</e> and <e>V_GENERAL_SORTED_SET</e>.
 +
 +
Sometimes you want to use an object as a key in a table,
 +
but the content of the object can change, so you cannot derive a hash code (or order) from any of its attributes.
 +
EiffelBase2 provides a utility class <e>V_REFERENCE_HASHABLE</e>,
 +
which derives a hash code from the object's identity rather than its content.
 +
For example:
 +
<e>
 +
class
 +
    CAR
 +
inherit
 +
    V_REFERENCE_HASHABLE
 +
 +
feature -- Access
 +
    color: COLOR
 +
 +
    location: POINT
 +
 +
feature -- Modification
 +
...
 +
end
 +
 +
 +
class GUI_APPLICATION
 +
 +
feature
 +
    cars: V_HASH_TABLE [CAR, CAR_VIEW]
 +
            -- Cars in the city and their graphical views.
 +
 +
    escape_police (c: CAR)
 +
            -- Drive `c' away, repaint it and update its view.
 +
        require
 +
            car_in_city: cars.has_key (c)
 +
        do
 +
            c.move (100, 100) -- change location
 +
            c.repaint (white) -- change color
 +
            cars [c].update -- ... but still can be used to access its view
 +
        end
 +
end
 +
</e>
  
 
===Stream piping===
 
===Stream piping===
Line 110: Line 193:
 
         create array.make (1, 10)
 
         create array.make (1, 10)
 
         -- Fill array with random integers:
 
         -- Fill array with random integers:
         array.at_first.pipe (create {V_RANDOM})
+
         array.new_cursor.pipe (create {V_RANDOM})
 
         -- Fill array with values parsed from a string:
 
         -- Fill array with values parsed from a string:
         array.at_first.pipe (create {V_STRING_INPUT [INTEGER]}.make ("1 2 3 4 5 6 7 8 9 10", agent {STRING}.to_integer))
+
         array.new_cursor.pipe (create {V_STRING_INPUT [INTEGER]}.make ("1 2 3 4 5 6 7 8 9 10", agent {STRING}.to_integer))
 
         -- Print array elements into standard output:
 
         -- Print array elements into standard output:
         (create {V_STANDARD_OUTPUT}).pipe (array.at_first)
+
         (create {V_STANDARD_OUTPUT}).pipe (array.new_cursor)
 
         -- Prints "1 2 3 4 5 6 7 8 9 10 "
 
         -- Prints "1 2 3 4 5 6 7 8 9 10 "
 
     end
 
     end
 
</e>
 
</e>
+
 
 
==Model-based contracts==
 
==Model-based contracts==
 
===Models===
 
===Models===
Line 125: Line 208:
 
Such mathematical objects in the program are represented by ''model classes'', which are immutable and thus are straightforward translations of mathematical definitions. The Mathematical Model Library (MML), which is a part of EiffelBase2 contains model classes for sets, relations, maps, sequences and bags. Boolean and integer components of models are represented by standard classes <e>BOOLEAN</e> and <e>INTEGER</e>. Finally, arbitrary reference classes can be used as model components to denote the mathematical sort of references.
 
Such mathematical objects in the program are represented by ''model classes'', which are immutable and thus are straightforward translations of mathematical definitions. The Mathematical Model Library (MML), which is a part of EiffelBase2 contains model classes for sets, relations, maps, sequences and bags. Boolean and integer components of models are represented by standard classes <e>BOOLEAN</e> and <e>INTEGER</e>. Finally, arbitrary reference classes can be used as model components to denote the mathematical sort of references.
  
For example, a mathematical sequence is a model for a stack or a queue. A pair consisting of a sequence and an integer is a model for a list with an internal cursor.
+
For example, a mathematical sequence of elements is a model for a stack or a queue.  
 +
A triple consisting of a reference to the target container, the sequence of elements and an integer is a model for an iterator.
  
 
The value of each component of the model is defined by a ''model query''. You define the model of the class in the source code by listing its model queries under the tag <e>model</e> in the class's <e>note</e> clause. For example:
 
The value of each component of the model is defined by a ''model query''. You define the model of the class in the source code by listing its model queries under the tag <e>model</e> in the class's <e>note</e> clause. For example:
 
<e>
 
<e>
 
note
 
note
     model: sequence, index
+
     model: target, sequence, index
class LIST [G]
+
 
 +
class V_LIST_ITERATOR [G]
 +
 
 
feature -- Access
 
feature -- Access
 +
 +
    target: V_LIST
 +
            -- List to iterate over.
 +
        deferred
 +
        end
 +
 
     index: INTEGER
 
     index: INTEGER
             -- Cursor position
+
             -- Current position.
 
         deferred
 
         deferred
 
         end
 
         end
Line 140: Line 232:
 
feature -- Specification
 
feature -- Specification
 
     sequence: MML_SEQUENCE [G]
 
     sequence: MML_SEQUENCE [G]
             -- Sequence of list elements
+
             -- Sequence of elements in `target'.
 
         note
 
         note
 
             status: specification
 
             status: specification
         ...
+
         deferred
 +
        end
 
end
 
end
 
</e>
 
</e>
Here we declared the model of class <e>LIST</e> consisting of two components: a mathematical sequence of type <e>MML_SEQUENCE</e> and an integer index. As you can see, model queries are not necessarily introduced specifically for specification purposes (as is the case with <e>sequence</e>); regular queries meant to be called from the code can be also reused as model queries (as <e>index</e> in this example). We attach a <e>status: specification</e> note to a query to indicate that its primary purpose is specification.
+
 
 +
Here we declared the model of class <e>V_LIST_ITERATOR</e> consisting of tree components:  
 +
a reference (to <e>V_LIST</e>), a mathematical sequence of type <e>MML_SEQUENCE</e> and an integer index.  
 +
As you can see, model queries are not necessarily introduced specifically for specification purposes (as is the case with <e>sequence</e>);  
 +
regular queries meant to be called from the code can be also reused as model queries (as <e>target</e> and <e>index</e> in this example).  
 +
We attach a <e>status: specification</e> note to a query to indicate that its primary purpose is specification.
 +
Those annotations can be used by tools to check, if a specification-only feature accidentally got called from non-specification code,
 +
and to remove such features from the code before compiling it.
  
 
===Contracts===
 
===Contracts===
The purpose of introducing model queries is to define the postconditions of the regular features in their terms. For queries we define their result (or the model of the result, in case the query returns a fresh object) as a function of the model of <e>Current</e> and (the models of) the arguments. Definitions of zero-argument queries, as usual, can be moved to the class invariant. For commands we define their effects on the model queries of <e>Current</e> and the arguments. If a model query is not mentioned in the postcondition of a command, it is equivalent to stating that it's not modified.  
+
The purpose of introducing model queries is to define the postconditions of regular features in their terms.  
 +
For queries we define their result (or the model of the result, in case the query returns a fresh object) as a function of the model of <e>Current</e> and (the models of) the arguments.  
 +
For commands we define their effects on the model queries of <e>Current</e> and the arguments.
 +
We also supply them with <e>modify</e> clauses that list all the model queries whose values are allowed to be changed by the command.
 +
These clauses can be used by tools to generate additional postconditions <e>m = old m</e>
 +
for each model query <e>m</e> of <e>Current</e> and other arguments that is not mentioned in the <e>modify</e> clause.
  
The model-based contracts approach does not constrain the way in which you write preconditions: it is not necessary to express them through model queries if they can be conveniently expressed otherwise.
+
The model-based contracts approach does not constrain the way in which you write preconditions:  
 +
it is not necessary to express them through model queries if they can be conveniently expressed otherwise.
  
 
Class invariants in the model-based contracts approach constrain the values of model queries to make them reflect precisely the abstract state space of the class.
 
Class invariants in the model-based contracts approach constrain the values of model queries to make them reflect precisely the abstract state space of the class.
  
Let us add a couple of features and model-based contracts into the class <e>LIST</e> shown above:
+
Let us add a couple of features and model-based contracts into the class <e>V_LIST_ITERATOR</e> shown above:
 
<e>
 
<e>
 
note
 
note
     model: sequence, index
+
     model: target, sequence, index
class LIST [G]
+
 
 +
class V_LIST_ITERATOR [G]
  
 
feature -- Access
 
feature -- Access
 +
 
     item: G
 
     item: G
             -- Item at current position
+
             -- Item at current position.
 
         require
 
         require
 
             not_off: not off
 
             not_off: not off
Line 173: Line 281:
  
 
feature -- Status report
 
feature -- Status report
 +
 
     off: BOOLEAN
 
     off: BOOLEAN
             -- Is cursor off all elements?
+
             -- Is current position off scope?
 
         deferred
 
         deferred
 
         ensure
 
         ensure
             definition: not sequence.domain [index]
+
             definition: Result = not sequence.domain [index]
 
         end
 
         end
 
...
 
...
  
feature -- Element change
+
feature -- Extension
     put_right (v: G)
+
 
             -- Put `v' to the right of the cursor
+
     extend_right (v: G)
 +
             -- Insert `v' to the right of current position.
 +
            -- Do not move cursor.
 +
        note
 +
            modify: sequence
 
         require
 
         require
             not_after: not after
+
             not_off: not off
        deferred
+
deferred
        ensure
+
ensure
            sequence_effect: sequence |=| old (sequence.front (index).extended (v) + sequence.tail (index + 1))
+
    sequence_effect: sequence |=| old (sequence.front (index) & v + sequence.tail (index + 1))
            index_effect: index = old index
+
end
        end
+
 
...
 
...
  
Line 201: Line 313:
 
If a class <e>B</e> inherits from <e>A</e> it is free to choose, whether to reuse each of <e>A</e>'s model queries to represent its own model, as well as introduce new model queries. If <e>B</e> does not reuse an <e>A</e>'s model query is has to provide a ''linking invariant'': a definition of the old model query in terms of <e>B</e>'s model. Linking invariants explain the parent's model in terms of the heir's model and thus make sure that the inherited model-based contracts make sense in the heir.
 
If a class <e>B</e> inherits from <e>A</e> it is free to choose, whether to reuse each of <e>A</e>'s model queries to represent its own model, as well as introduce new model queries. If <e>B</e> does not reuse an <e>A</e>'s model query is has to provide a ''linking invariant'': a definition of the old model query in terms of <e>B</e>'s model. Linking invariants explain the parent's model in terms of the heir's model and thus make sure that the inherited model-based contracts make sense in the heir.
  
For example, suppose that <e>LIST</e> inherits directly from <e>CONTAINER</e>, whose model is a bag:
+
For example, look at <e>V_SET</e>, which inherits directly from <e>V_CONTAINER</e>:
 
<e>
 
<e>
 
note
 
note
 
     model: bag
 
     model: bag
class CONTAINER [G]
+
class V_CONTAINER [G]
 
...
 
...
 
end
 
end
  
 
note
 
note
     model: sequence, index
+
     model: set
class LIST [G]
+
class V_SET[G]
 
...
 
...
 
invariant
 
invariant
 
     ...
 
     ...
     bag_domain_definition: bag.domain = sequence.range
+
     bag_domain_definition: bag.domain |=| set
     bag_definition: bag.domain.for all (agent (x: G): BOOLEAN
+
     bag_definition: bag.is_constant (1)
        do Result := bag [x] = sequence.occurrences (x) end)
+
 
end
 
end
 
</e>
 
</e>
Here the linking invariant, provided as part of the class invariant in <e>LIST</e> completely defines an old model query <e>bag</e> in terms of a new model query <e>sequence</e>.
+
Here the linking invariant, provided as part of the class invariant in <e>V_SET</e> completely defines an old model query <e>bag</e> in terms of a new model query <e>set</e>.
  
 
==Status and roadmap==
 
==Status and roadmap==
Line 226: Line 337:
 
EiffelBase2 is currently being developed as a project at ETH Zurich.
 
EiffelBase2 is currently being developed as a project at ETH Zurich.
  
===ToDo list===
+
It has been used in the following projects:
* Add immutable and mutable strings.
+
* Traffic 4: modeling public transportation in a city [https://bitbucket.org/nadiapolikarpova/traffic https://bitbucket.org/nadiapolikarpova/traffic]
* Make model class implementation more efficient.
+
* Add classes and directories.
+
* Rewrite loops using the <e>across</e> where appropriate.
+
* Add an iterator over keys for <e>TABLE</e>.
+
* Make the library void-safe.
+
* Implement more efficient data structures: e.g., tables with fast lookup both ways, heaps, skip lists, treaps, etc.
+

Latest revision as of 02:04, 30 April 2014


Overview

EiffelBase2 is a general-purpose data structures library for Eiffel. It is intended as the future replacement for the EiffelBase library, which has for many years played a central role in Eiffel development.

Download

The latest version of the EiffelBase2 source code is available from the repository: https://bitbucket.org/nadiapolikarpova/eiffelbase2/

The source code is divided into two clusters:

  • structures - Data Structures, the core of EiffelBase2. The rest of current document is about this cluster.
  • mml - Mathematical Model Library: a library of immutable classes used in model-based contracts (see below).

Goals

The design goals for EiffelBase2 are:

  • Verifiability. The library is designed to allow proofs of correctness.
  • Complete specifications. Partly as a result of the verifiability goal, but also for clarity and documentation, the contracts associated with classes and features should describe the relevant semantics in its entirety.
  • Simple and consistent hierarchy, in particular avoidance of descendant hiding and of "taxomania" (classes not representing a meaningful abstraction, unnecessary inheritance links).

Design

On the top level EiffelBase2 differentiates between containers and streams. A container is a finite storage of values, while a stream provides linear access to a set of values. A stream is not necessarily bound to a container, e.g. RANDOM stream observes an infinite sequence of pseudo-random numbers. Streams that traverse containers are called iterators.

Below you can find the class diagram of EiffelBase2, split into two hierarchies: containers and streams/iterators. All EiffelBase2 class names start with V_ (for Verified), but the prefix is omitted in the current document for brevity.

In the diagrams below asterisk and italics font indicates a deferred class. Lighter fill color indicates that the class provides an immutable interface to the data, in other words, it is impossible to modify the content of the container using this interface.

Container class hierarchy
Iterator class hierarchy

Usage examples

Immutable interfaces

With immutable interfaces you can give your clients read-only access to a container you store:

class TRAM_LINE
 
feature -- Access
 
    stations: V_SEQUENCE [STATION]
            -- Stations on the line.
            -- (Clients can traverse the stations, but cannot replace them, remove or add new ones.)
        do
            Result := station_list
        end
 
feature {NONE} -- Implementation
 
    station_list: V_LIST [STATION]
            -- List of line stations.
end

Iterators

Here is how you can iterate through any container:

do_something (container: V_CONTAINER [INTEGER])
    do
        across
            container as i
        loop
            print (i.item)            
        end
    end

The same thing using the explicit syntax:

do_something (container: V_CONTAINER [INTEGER])
    local
        i: V_ITERATOR [INTEGER]
    do
        from
            i := container.new_cursor
        until
            i.after
        loop
            print (i.item)
            i.forth
        end
    end

Here is some more advanced stuff you can do with lists:

do_something (list: V_LIST [INTEGER])
    local
        i: V_LIST_ITERATOR [INTEGER]
    do
        -- Find the last 0 at or before position 5:
        list.at (5).search_back (0)
        -- Find the first positive element at or after position 5:
        i := list.at (5)
        i.satisfy_forth (agent (x: INTEGER): BOOLEAN do Result := x > 0 end)
        -- And insert a 0 after it:
        i.extend_right (0)
    end

Sets and tables

Here is how you create and use a simple hash table (keys must inherit from HASHABLE):

do_something
    local
        table: V_HASH_TABLE [STRING, INTEGER]
    do
        create table.with_object_equality
        table ["cat"] := 1
        table ["dog"] := 2
        print (table ["cat"] + table ["dog"])
        -- Prints "3"
    end

If you need a custom hash function or equivalence relation on keys, you can use V_GENERAL_HASH_TABLE, for example:

do_something
    local
        table: V_GENERAL_HASH_TABLE [STRING, INTEGER]
    do
        -- Create case-insensitive table:
        create table.make (
            agent {STRING}.is_case_insensitive_equal,
            agent (s: STRING): INTEGER do Result := s.as_lower.hash_code end
        )
        table ["cat"] := 1
        table ["dog"] := 2
        print (table ["CAT"] + table ["dog"])
        -- Prints "3"
    end

Similar style applies to V_HASH_SET and V_GENERAL_HASH_SET, V_SORTED_TABLE and V_GENERAL_SORTED_TABLE, V_SORTED_SET and V_GENERAL_SORTED_SET.

Sometimes you want to use an object as a key in a table, but the content of the object can change, so you cannot derive a hash code (or order) from any of its attributes. EiffelBase2 provides a utility class V_REFERENCE_HASHABLE, which derives a hash code from the object's identity rather than its content. For example:

class 
    CAR
inherit 
    V_REFERENCE_HASHABLE
 
feature -- Access
    color: COLOR
 
    location: POINT
 
feature -- Modification
...
end
 
 
class GUI_APPLICATION
 
feature
    cars: V_HASH_TABLE [CAR, CAR_VIEW]
            -- Cars in the city and their graphical views.
 
    escape_police (c: CAR)
            -- Drive `c' away, repaint it and update its view.
        require
            car_in_city: cars.has_key (c)
        do
            c.move (100, 100) -- change location
            c.repaint (white) -- change color
            cars [c].update -- ... but still can be used to access its view
        end
end

Stream piping

Iterators in EiffelBase2 are a special case of streams. Sometimes you can avoid writing a loop by piping an input stream into an output stream, for example:

do_something
    local
        array: V_ARRAY [INTEGER]
    do
        create array.make (1, 10)
        -- Fill array with random integers:
        array.new_cursor.pipe (create {V_RANDOM})
        -- Fill array with values parsed from a string:
        array.new_cursor.pipe (create {V_STRING_INPUT [INTEGER]}.make ("1 2 3 4 5 6 7 8 9 10", agent {STRING}.to_integer))
        -- Print array elements into standard output:
        (create {V_STANDARD_OUTPUT}).pipe (array.new_cursor)
        -- Prints "1 2 3 4 5 6 7 8 9 10 "
    end

Model-based contracts

Models

EiffelBase2 is specified using the model-based contracts specification method. This method prescribes that each class defines a model - a mathematical object that represents explicitly its abstract state space. The model of a class is expressed as tuple of one or more predefined mathematical objects: booleans, integers, sets, relations, maps, sequences or bags. We also introduce a special mathematical sort for object IDs (references).

Such mathematical objects in the program are represented by model classes, which are immutable and thus are straightforward translations of mathematical definitions. The Mathematical Model Library (MML), which is a part of EiffelBase2 contains model classes for sets, relations, maps, sequences and bags. Boolean and integer components of models are represented by standard classes BOOLEAN and INTEGER. Finally, arbitrary reference classes can be used as model components to denote the mathematical sort of references.

For example, a mathematical sequence of elements is a model for a stack or a queue. A triple consisting of a reference to the target container, the sequence of elements and an integer is a model for an iterator.

The value of each component of the model is defined by a model query. You define the model of the class in the source code by listing its model queries under the tag model in the class's note clause. For example:

note
    model: target, sequence, index
 
class V_LIST_ITERATOR [G]
 
feature -- Access
 
    target: V_LIST
            -- List to iterate over.
        deferred
        end
 
    index: INTEGER
            -- Current position.
        deferred
        end
...
feature -- Specification
    sequence: MML_SEQUENCE [G]
            -- Sequence of elements in `target'.
        note
            status: specification
        deferred
        end
end

Here we declared the model of class V_LIST_ITERATOR consisting of tree components: a reference (to V_LIST), a mathematical sequence of type MML_SEQUENCE and an integer index. As you can see, model queries are not necessarily introduced specifically for specification purposes (as is the case with sequence); regular queries meant to be called from the code can be also reused as model queries (as target and index in this example). We attach a status: specification note to a query to indicate that its primary purpose is specification. Those annotations can be used by tools to check, if a specification-only feature accidentally got called from non-specification code, and to remove such features from the code before compiling it.

Contracts

The purpose of introducing model queries is to define the postconditions of regular features in their terms. For queries we define their result (or the model of the result, in case the query returns a fresh object) as a function of the model of Current and (the models of) the arguments. For commands we define their effects on the model queries of Current and the arguments. We also supply them with modify clauses that list all the model queries whose values are allowed to be changed by the command. These clauses can be used by tools to generate additional postconditions m = old m for each model query m of Current and other arguments that is not mentioned in the modify clause.

The model-based contracts approach does not constrain the way in which you write preconditions: it is not necessary to express them through model queries if they can be conveniently expressed otherwise.

Class invariants in the model-based contracts approach constrain the values of model queries to make them reflect precisely the abstract state space of the class.

Let us add a couple of features and model-based contracts into the class V_LIST_ITERATOR shown above:

note
    model: target, sequence, index
 
class V_LIST_ITERATOR [G]
 
feature -- Access
 
    item: G
            -- Item at current position.
        require
            not_off: not off
        deferred
        ensure
            definition: Result = sequence [index]
        end
...
 
feature -- Status report
 
    off: BOOLEAN
            -- Is current position off scope?
        deferred
        ensure
            definition: Result = not sequence.domain [index]
        end
...
 
feature -- Extension
 
    extend_right (v: G)
            -- Insert `v' to the right of current position.
            -- Do not move cursor.
        note
            modify: sequence
        require
            not_off: not off
	deferred
	ensure
	    sequence_effect: sequence |=| old (sequence.front (index) & v + sequence.tail (index + 1))
	end
...
 
invariant
    index_in_range: 0 <= index and index <= sequence.count + 1
end

Inheritance

If a class B inherits from A it is free to choose, whether to reuse each of A's model queries to represent its own model, as well as introduce new model queries. If B does not reuse an A's model query is has to provide a linking invariant: a definition of the old model query in terms of B's model. Linking invariants explain the parent's model in terms of the heir's model and thus make sure that the inherited model-based contracts make sense in the heir.

For example, look at V_SET, which inherits directly from V_CONTAINER:

note
    model: bag
class V_CONTAINER [G]
...
end
 
note
    model: set
class V_SET[G]
...
invariant
    ...
    bag_domain_definition: bag.domain |=| set
    bag_definition: bag.is_constant (1)
end

Here the linking invariant, provided as part of the class invariant in V_SET completely defines an old model query bag in terms of a new model query set.

Status and roadmap

EiffelBase2 is currently being developed as a project at ETH Zurich.

It has been used in the following projects: