Link
You can think of the class invariant as a health criterion, which must
be fulfilled by all objects in between operations. As a precondition
of every public operation of the class, it can therefore be assumed
that the class invariant holds. In addition, it can be assumed as a
postcondition of every public operation that the class invariant
holds. In this sense, the class invariant serves as a general
strengthening of both the precondition and the postcondition of
public operations in the class. The effective precondition is the
formulated precondition in conjunction with the class invariant.
Similarly, the effective postcondition is the formulated
postcondition in conjunction with the class invariant.
public class Server
{
// other code ommited
public Output Foo(Input cmdIn)
{
...
return cmdOut;
}
}
public class Caller
{
// other code ommited
/* calls Server.Foo */
public void Call()
{...}
}
public class Input
{
// other code ommited
public int Length
{...}
}
public class Output
{
// other code ommited
public int Length
{...}
}
1. If class invariant is defined on Server class:
a) Preconditions are typically formulated in terms of the formal parameters of the called operation, so how can class invariant strengthen method's ( Foo's ) preconditions?
b) Postcondition is formulated in terms of called method's return value, so how can class invariant strengthen method's ( Foo's ) postconditions?
2. Can class invariant defined on Caller class in any way strengthen Foo's preconditions or postconditions?
3. If class invariant is defined on Foo's cmdIn parameter:
a) If precondition on Foo states that cmdIn.Length is within range 1-20, but one of class invariants defined on Input states that Input.Length should be within range 2-19, then Foo's precondition was indeed strenghten?
b) Isn't the logic in a) a bit flawed, since if class invariant already states that Input.Length should be within range 2-19, isn't it then an error for Foo to define a precondition which isn't always be true ( cmdIn.Length can't hold values 1 or 20 )
c) But if class invariant defined on Input states that Input.Length should be within range 0-100, then Foo's precondition isn't strengthen?
d) Can class invariants defined on cmdIn also somehow strengthen Foo's postcondition?
4. If class invariant is defined on Foo's return value
a) If postcondition on Foo states that cmdOut.Length is within range 1-20, but one of class invariants defined on Output states that Output.Length should be within range 2-19, then Foo's postcondition was indeed strengthen?
b) But if invariant defined on Output states that Output.Length should be within range 0-100, then Foo's postcondition wasn't strengthen?
c) Can class invariants defined on Output also somehow strengthen Foo's precondition?
5. But I get the impression that quoted article meant to say that just by having a class invariant ( and even if this invariant doesn't in any way operate ( directly or indirectly ) on Foo's parameters and/or return value, it would still strengthen Foo's preconditions and postcondition? If that's what article is actually implying, how is that possible?
thanks
a) Preconditions are typically formulated in terms of the formal
parameters of the called operation, so how can class invariant
strengthen method's ( Foo's ) preconditions?
I suspect that's key to your misunderstanding. Pre-conditions may include formal parameters - but are not restricted to them. They can - and often do - also refer to class features (attributes/operations). In general, the combination of invariants and pre-condition defines a set of constraints that must be satisfied before an operation is obliged to meet its post-condition when called. Similarly, an operation must guarantee that both its post condition and any invariants will be satisfied when it completes. Take the example of a Bounded Buffer:
Class BoundedBuffer<T> {
public int max // max #items the buffer can hold
public int count // how many items currently in the buffer
void push(T item) {...}
T pop() {...}
}
A pre-condition for push() would be that the buffer has not reached its maximum size:
pre: count < max
So here the pre-condition doesn't even mention the operation's formal parameter. We can also state an invariant for the Buffer:
inv: count >=0 //can't have -ve number of elements in the buffer
It strengthens the pre-condition because it entends what must be true before the push() operation is obliged to meet its post condition. The two clauses are logically ANDed together. So the effective pre-condition is count >=0 AND count < max. That's a stronger (more restrictive) constraint than the pre-condition alone.
Note the concept isn't restricted to situations where the pre-condition refers to class features. Let's add the constraint that the size of any individual item being added to the buffer must be less than some upper limit:
pre: count < max AND item.size() <= MAX_ITEM_SIZE
Adding the invariant still strengthens the effective pre-condition to become:
pre: count < max AND item.size() <= MAX_ITEM_SIZE AND count >=0
So in summary: invariants must hold before an operation is invoked and after an operation completes. Hence they strengthen both.
Can class invariant defined on Caller class in any way strengthen Foo's preconditions or postconditions?
No. Invariants apply to the class they are defined on only.
Answers to your remaining questions flow logically from above.
hth.
Related
I have doubts about where I should check my invariants...
For example, I have a Question aggregate with the following invariant:
The question's text must be not null
The question's text must have a length between 100 and 500 characters
I have read that the best place to check the invariants is in aggregate's constructor, but I have also read it would be recommended push domain logic in value objects, for example.
A possible aggregate's implementation could be:
public class Question {
private final id: QuestionId;
private final text: QuestionText
public Question(id: String, text: String) {
// verify invariants
ensureLengthText(text); // this method verifies the invariant
this.id = new QuestionId(id);
this.text = new QuestionText(text);
}
}
QuestionId and QuestionText are value objects.
In this case, in the aggregate we can see the invariant explicitly.
But, if we push that invariant logic in QuestionText value object, in the aggregate we will not see that invariant... then, what would be the best approach?
If you can easily enforce an invariant (e.g. "Question texts cannot be shorter than 100 characters nor longer than 500 characters") in a value object, I recommend doing so. Question inherits that invariant (and any and all others from QuestionText) by saying that text is a QuestionText.
I have a superclass called A and a subclass called B that inherits from A. The superclass's constructor looks like this:
A(String name, char displayChar, int hitPoints, Behaviour behaviour)
{
this.name = name;
this.displayChar = displayChar;
this.hitPoints = hitPoints
addBehaviour(behaviour);
}
A has attributes of name, displayChar, hitPoints, behaviour and has a method that calls addBehaviour which adds the behaviour to the object.
The subclass, B's constructor looks like this:
B(String name) {super(name, char 'b', 10, new WalkBehaviour()); }
Now my question is, does subclass B have an attribute of WalkBehaviour?
How would the UML diagram look like for this scenario? I know B inherits from A and A has Behaviour but does B has WalkBehaviour in this case? Since B doesn't have an instance variable of type WalkBehaviour in its class but only passes WalkBehaviour through its superclass's constructor.
does subclass B have an attribute of WalkBehaviour?
No. There is none declared. The superclass will do something with that new object but obviously it's hidden in the mist of its implementation.
Inheritance is nothing that involves multiple object creation. Your B instance is just a single one which does have attributes and operations like its super class.
Thus, in a SD, you will see only one life line for B:
As you can see the B instance will just issue a self-call to the super class's constructor.
Note: as #AxelScheithauer pointed out in the comment the super class will invoke addBehavior which can (but must not) be shown in the SD:
I understand that a precondition, in context of Desing by contract/Liskov principle, is something that should be true before the code is called, e.g. the caller is responsible for that. Also, the author of the Eiffel language stated that most people do put another verification check into the called cade, simply as means of defensive programming.
Some time ago I read a question with a code similar to this:
void X(int value)
{
if (value > 100)
{do something...}
}
Some commenters argued that the if statement is not a precondition but I do not think that is right - if the contract states V must be 100, then this is verifying the precondition additionally and if a class is derived from this type and changes to v > 200, it would be strenghtening the precondition and thus violating the Liskov principle. Or isn't that the case?
As you said, a precondition is defined as a condition that must always be true before the proceeding code executes.
This means that anything that checks for a condition at the beginning of a function before other code is executed, then it is considered a precondition.
Example:
//We will do something cool here
//With an integer input
int doSomethingCool(final int input)
{
//Wait, what if input is null or less than 100?
if(null == input || input < 100)
{
//Return a -1 to signify an issue
return -1;
}
//The cool bit of multiplying by 50. So cool.
final int results = input * 50;
//Return the results;
return results;
}
In the example, the function, input is checked before anything else is executed. So long as the conditions are met, then the rest of the code will execute.
Bad Example:
//We will do something cool here
//With an integer input
int doSomethingCool(final int input)
{
//Want to make sure input is not null and larger than 100
if(null != input && input > 100)
{
//The cool bit of multiplying by 50. So cool.
final int results = input * 50;
//Return the results;
return results;
}
//Return a -1 to signify an issue because the
//preconditions were not met for some reason
return -1;
}
In the example, the precondition is to check that input is not null and larger than 100. This is a bad precondition because it could introduce unnecessary nesting of ifs and loops.
Preconditions should do checks and only return when the checks fail. There should be no work done in a precondition.
In keeping with the Liskov Substitution Principle, if type S is a subtype of type T, then type T can be replaced with type S. If type S overrides doSomethingCool and changes the precondition, then it is in violation, because type T is the base definition and defines the intended conditions that must be met.
Now for your answer
Yes, simple conditions still count as preconditions. As long as they are at the front of all other code that uses the variable, the condition is as what is needed by the program. Also, if the function is in a subtype and is overriding the parent class, then it should not change the precondition.
However, do not surround the code that you need to run within the precondition. That is bad practice. If value needs to be larger than 100, check value < 100 and set a return in the if check.
Haskell enables one to construct algebraic data types using type constructors and data constructors. For example,
data Circle = Circle Float Float Float
and we are told this data constructor (Circle on right) is a function that constructs a circle when give data, e.g. x, y, radius.
Circle :: Float -> Float -> Float -> Circle
My questions are:
What is actually constructed by this function, specifically?
Can we define the constructor function?
I've seen Smart Constructors but they just seem to be extra functions that eventually call the regular constructors.
Coming from an OO background, constructors, of course, have imperative specifications. In Haskell, they seem to be system-defined.
In Haskell, without considering the underlying implementation, a data constructor creates a value, essentially by fiat. “ ‘Let there be a Circle’, said the programmer, and there was a Circle.” Asking what Circle 1 2 3 creates is akin to asking what the literal 1 creates in Python or Java.
A nullary constructor is closer to what you usually think of as a literal. The Boolean type is literally defined as
data Boolean = True | False
where True and False are data constructors, not literals defined by Haskell grammar.
The data type is also the definition of the constructor; as there isn't really anything to a value beyond the constructor name and its arguments, simply stating it is the definition. You create a value of type Circle by calling the data constructor Circle with 3 arguments, and that's it.
A so-called "smart constructor" is just a function that calls a data constructor, with perhaps some other logic to restrict which instances can be created. For example, consider a simple wrapper around Integer:
newtype PosInteger = PosInt Integer
The constructor is PosInt; a smart constructor might look like
mkPosInt :: Integer -> PosInteger
mkPosInt n | n > 0 = PosInt n
| otherwise = error "Argument must be positive"
With mkPosInt, there is no way to create a PosInteger value with a non-positive argument, because only positive arguments actually call the data constructor. A smart constructor makes the most sense when it, and not the data constructor, is exported by a module, so that a typical user cannot create arbitrary instances (because the data constructor does not exist outside the module).
Good question. As you know, given the definition:
data Foo = A | B Int
this defines a type with a (nullary) type constructor Foo and two data constructors, A and B.
Each of these data constructors, when fully applied (to no arguments in the case of A and to a single Int argument in the case of B) constructs a value of type Foo. So, when I write:
a :: Foo
a = A
b :: Foo
b = B 10
the names a and b are bound to two values of type Foo.
So, data constructors for type Foo construct values of type Foo.
What are values of type Foo? Well, first of all, they are different from values of any other type. Second, they are wholly defined by their data constructors. There is a distinct value of type Foo, different from all other values of Foo, for each combination of a data constructor with a set of distinct arguments passed to that data constructor. That is, two values of type Foo are identical if and only if they were constructed with the same data constructor given identical sets of arguments. ("Identical" here means something different from "equality", which may not necessarily be defined for a given type Foo, but let's not get into that.)
That's also what makes data constructors different from functions in Haskell. If I have a function:
bar :: Int -> Bool
It's possible that bar 1 and bar 2 might be exactly the same value. For example, if bar is defined by:
bar n = n > 0
then it's obvious that bar 1 and bar 2 (and bar 3) are identically True. Whether the value of bar is the same for different values of its arguments will depend on the function definition.
In contrast, if Bar is a constructor:
data BarType = Bar Int
then it's never going to be the case that Bar 1 and Bar 2 are the same value. By definition, they will be different values (of type BarType).
By the way, the idea that constructors are just a special kind of function is a common viewpoint. I personally think this is inaccurate and causes confusion. While it's true that constructors can often be used as if they are functions (specifically that they behave very much like functions when used in expressions), I don't think this view stands up under much scrutiny -- constructors are represented differently in the surface syntax of the language (with capitalized identifiers), can be used in contexts (like pattern matching) where functions cannot be used, are represented differently in compiled code, etc.
So, when you ask "can we define the constructor function", the answer is "no", because there is no constructor function. Instead, a constructor like A or B or Bar or Circle is what it is -- something different from a function (that sometimes behaves like a function with some special additional properties) which is capable of constructing a value of whatever type the data constructor belongs to.
This makes Haskell constructors very different from OO constructors, but that's not surprising since Haskell values are very different from OO objects. In an OO language, you can typically provide a constructor function that does some processing in building the object, so in Python you might write:
class Bar:
def __init__(self, n):
self.value = n > 0
and then after:
bar1 = Bar(1)
bar2 = Bar(2)
we have two distinct objects bar1 and bar2 (which would satify bar1 != bar2) that have been configured with the same field values and are in some sense "equal". This is sort of halfway between the situation above with bar 1 and bar 2 creating two identical values (namely True) and the situation with Bar 1 and Bar 2 creating two distinct values that, by definition, can't possibly be the "same" in any sense.
You can never have this situation with Haskell constructors. Instead of thinking of a Haskell constructor as running some underlying function to "construct" an object which might involve some cool processing and deriving of field values, you should instead think of a Haskell constructor as a passive tag attached to a value (which may also contain zero or more other values, depending on the arity of the constructor).
So, in your example, Circle 10 20 5 doesn't "construct" an object of type Circle by running some function. It directly creates a tagged object that, in memory, will look something like:
<Circle tag>
<Float value 10>
<Float value 20>
<Float value 5>
(or you can at least pretend that's what it looks like in memory).
The closest you can come to OO constructors in Haskell is using smart constructors. As you note, eventually a smart constructor just calls a regular constructor, because that's the only way to create a value of a given type. No matter what kind of bizarre smart constructor you build to create a Circle, the value it constructs will need to look like:
<Circle tag>
<some Float value>
<another Float value>
<a final Float value>
which you'll need to construct with a plain old Circle constructor call. There's nothing else the smart constructor could return that would still be a Circle. That's just how Haskell works.
Does that help?
I’m going to answer this in a somewhat roundabout way, with an example that I hope illustrates my point, which is that Haskell decouples several distinct ideas that are coupled in OOP under the concept of a “class”. Understanding this will help you translate your experience from OOP into Haskell with less difficulty. The example in OOP pseudocode:
class Person {
private int id;
private String name;
public Person(int id, String name) {
if (id == 0)
throw new InvalidIdException();
if (name == "")
throw new InvalidNameException();
this.name = name;
this.id = id;
}
public int getId() { return this.id; }
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
}
In Haskell:
module Person
( Person
, mkPerson
, getId
, getName
, setName
) where
data Person = Person
{ personId :: Int
, personName :: String
}
mkPerson :: Int -> String -> Either String Person
mkPerson id name
| id == 0 = Left "invalid id"
| name == "" = Left "invalid name"
| otherwise = Right (Person id name)
getId :: Person -> Int
getId = personId
getName :: Person -> String
getName = personName
setName :: String -> Person -> Either String Person
setName name person = mkPerson (personId person) name
Notice:
The Person class has been translated to a module which happens to export a data type by the same name—types (for domain representation and invariants) are decoupled from modules (for namespacing and code organisation).
The fields id and name, which are specified as private in the class definition, are translated to ordinary (public) fields on the data definition, since in Haskell they’re made private by omitting them from the export list of the Person module—definitions and visibility are decoupled.
The constructor has been translated into two parts: one (the Person data constructor) that simply initialises the fields, and another (mkPerson) that performs validation—allocation & initialisation and validation are decoupled. Since the Person type is exported, but its constructor is not, this is the only way for clients to construct a Person—it’s an “abstract data type”.
The public interface has been translated to functions that are exported by the Person module, and the setName function that previously mutated the Person object has become a function that returns a new instance of the Person data type that happens to share the old ID. The OOP code has a bug: it should include a check in setName for the name != "" invariant; the Haskell code can avoid this by using the mkPerson smart constructor to ensure that all Person values are valid by construction. So state transitions and validation are also decoupled—you only need to check invariants when constructing a value, because it can’t change thereafter.
So as for your actual questions:
What is actually constructed by this function, specifically?
A constructor of a data type allocates space for the tag and fields of a value, sets the tag to which constructor was used to create the value, and initialises the fields to the arguments of the constructor. You can’t override it because the process is completely mechanical and there’s no reason (in normal safe code) to do so. It’s an internal detail of the language and runtime.
Can we define the constructor function?
No—if you want to perform additional validation to enforce invariants, you should use a “smart constructor” function which calls the lower-level data constructor. Because Haskell values are immutable by default, values can be made correct by construction; that is, when you don’t have mutation, you don’t need to enforce that all state transitions are correct, only that all states themselves are constructed correctly. And often you can arrange your types so that smart constructors aren’t even necessary.
The only thing you can change about the generated data constructor “function” is making its type signature more restrictive using GADTs, to help enforce more invariants at compile-time. And as a side note, GADTs also let you do existential quantification, which lets you carry around encapsulated/type-erased information at runtime, exactly like an OOP vtable—so this is another thing that’s decoupled in Haskell but coupled in typical OOP languages.
Long story short (too late), you can do all the same things, you just arrange them differently, because Haskell provides the various features of OOP classes under separate orthogonal language features.
The Alloy 4 grammar allows signature declarations (and some other things) to carry a private keyword. It also allows Allow specifications to contain enumeration declarations of the form
enum nephews { hughie, louis, dewey }
enum ducks { donald, daisy, scrooge, nephews }
The language reference doesn't (as far as I can tell) describe the meaning of either the private keyword or the enum construct.
Is there documentation available? Or are they in the grammar as constructs that are reserved for future specification?
This is my unofficial understanding of those two keywords.
enum nephews { hughie, louis, dewey }
is semantically equivalent to
open util/ordering[nephews] as nephewsOrd
abstract sig nephews {}
one sig hughie extends nephews {}
one sig louis extends nephews {}
one sig dewey extends nephews {}
fact {
nephewsOrd/first = hughie
nephewsOrd/next = hughie -> louis + louis -> dewey
}
The private keyword means that if a sig has the private attribute, its label is private within the same module. The same applies for private fields and private functions.
In addition to the previous accepted answer, I'd like to add some useful insights coming from a one-week experience with Alloy on enums, in particular on the main differences with standard sig.
If you use abstract sig + extend, you'll come up with a model in which there are many sets corresponding to the same concept. Maybe an example could clarify it better.
Suppose somthing like
sig Car {
dameges: set Damage
}
You have the choice to use
abstract sig Damage {}
sig MajorDamage, MinorDamage extends Damage {}
vs
enum Damage {
MajorDamage, MinorDamage
}
In the first case we can come up wiht a model with different MinorDamage atoms (MinorDamage0, MinorDamage1, ...) associatet to Cars, while in the second case you always have only one MinorDamage to which different Cars can refer.
It could have some sense in this case to use an abstract sig + extend form (because you can decide to track different MinorDamage or MajorDamage elements).
On the other hand, if you want to have a currentState: set State, it could be better to use an
enum State {Damaged, Parked, Driven}
to map the concept, in order to have exactly three State to which each Car can refer to. In this way, in the Visualizer, you can decide to project your model on exactly one of the states and it will highlight all the Cars associated to this state. You can't do that with the abstract + extend construct, of course, because projecting over MajorDamage0 will highlight only the Car associated to that Damage and nothing else.
So, in conclusion, it really depends on what you have to do.
Also, keep in mind that if you have an enum composed by X elements and execute
run some_predicate for Y
where Y < X, Alloy produces no instance at all.
So, in our last example, we can't have a Y < 3.
As a last note, enums don't always appear in the Visualizer if you use the Magic Layout button, but as I said previously you can "project" your model over the enum and switch between the different elements of the enum.