Difference between revisions of "Dynamic type testing"

From Liberty Eiffel Wiki
Jump to navigation Jump to search
m (Typo)
 
(17 intermediate revisions by 6 users not shown)
Line 1: Line 1:
[[Category:Book]]
 
 
Sometimes, you need to decide at run-time if a variable
 
Sometimes, you need to decide at run-time if a variable
 
(or [[Glossary#Expression|expression]]) refers to an instance of a specific class or type.
 
(or [[Glossary#Expression|expression]]) refers to an instance of a specific class or type.
Well, it is always better to rely on [[dynamic dispatch]], but, in some situations, it can be necessary
+
Well, it is almost always better to rely on [[dynamic dispatch]], but, in some situations, it can be necessary
to test explicitly the [[Glossary#DynamicType|dynamic type]] of some expression.
+
to explicitly test the [[Glossary#DynamicType|dynamic type]] of some expression.
In such an uncommon situation, SmartEiffel provides you three operators for doing that:
+
While other languages provide a cast operator to do (unsafe) type casting, Liberty Eiffel provides you three operators for doing that:
the
+
the
 
[[#AssignmentTest|<tt>?:=</tt>]] assignment test operator,
 
[[#AssignmentTest|<tt>?:=</tt>]] assignment test operator,
 
the [[#ForcedAssignment|<tt>::=</tt>]] forced assignment statement as well as the
 
the [[#ForcedAssignment|<tt>::=</tt>]] forced assignment statement as well as the
Line 11: Line 10:
   
 
For some examples about dynamic type testing, check also the
 
For some examples about dynamic type testing, check also the
<tt>SmartEiffel/tutorial/downcasting.e</tt> file.
+
<tt>tutorial/downcasting.e</tt> file.
  +
  +
Please note that assignment testing cannot be used with [[Expanded_or_reference|expanded types]], for which [[dynamic dispatch]] is not applicable.
   
 
<div id="AssignmentTest">
 
<div id="AssignmentTest">
Line 19: Line 20:
 
allowing you to check if the given right-hand side expression refers to an object which conforms to
 
allowing you to check if the given right-hand side expression refers to an object which conforms to
 
some explicitly written type.
 
some explicitly written type.
As an example, you can use the <tt>?:=</tt> opeartor as follow to check that
+
As an example, you can use the <tt>?:=</tt> operator as follows to check that
some <tt>expression</tt> actually holds an instance which could be assigned into some
+
some <tt>expression</tt> actually holds an instance which could be assigned to some
 
variable declared with <tt>SOME_TYPE</tt>:
 
variable declared with <tt>SOME_TYPE</tt>:
 
if {SOME_TYPE} ?:= expression then
 
if {SOME_TYPE} ?:= expression then
Line 28: Line 29:
 
end
 
end
 
It evaluates <tt>expression</tt> and returns <tt>True</tt> if the resulting object
 
It evaluates <tt>expression</tt> and returns <tt>True</tt> if the resulting object
can be legally assigned into some variable declared of type <tt>SOME_TYPE</tt>.
+
can be legally assigned to some variable declared of type <tt>SOME_TYPE</tt>.
 
To take now a more concrete example using existing types of our library:
 
To take now a more concrete example using existing types of our library:
 
if {FAST_ARRAY[INTEGER]} ?:= my_collection then
 
if {FAST_ARRAY[INTEGER]} ?:= my_collection then
Line 51: Line 52:
 
end
 
end
 
The check performed by the compiler is that an expression of type <tt>SOME_TYPE</tt> could be
 
The check performed by the compiler is that an expression of type <tt>SOME_TYPE</tt> could be
legally assigned into some variable declared of the
+
legally assigned to some variable declared of the
 
[[Glossary#StaticType|static type]] of <tt>some_expression</tt>.
 
[[Glossary#StaticType|static type]] of <tt>some_expression</tt>.
 
Actually, this simply means that you trust the type system and that you really want to reject
 
Actually, this simply means that you trust the type system and that you really want to reject
invalid or to say the least, weird code.
+
invalid or, to say the least, weird code.
 
Because of the redefinition mechanism, the validation check for the <tt>?:=</tt> operator is only
 
Because of the redefinition mechanism, the validation check for the <tt>?:=</tt> operator is only
 
performed at the class where it appears, and is '''not''' revalidated in subtypes even if they
 
performed at the class where it appears, and is '''not''' revalidated in subtypes even if they
Line 60: Line 61:
 
The latest decision was driven by our experimentations.
 
The latest decision was driven by our experimentations.
 
We made this decision because the strict rule would reject mostly valid situations.
 
We made this decision because the strict rule would reject mostly valid situations.
Similars checks are performed for all other statements of the same family
+
Similar checks are performed for all other statements of the same family
 
(the [[#ForcedAssignment|<tt>::=</tt>]] statement
 
(the [[#ForcedAssignment|<tt>::=</tt>]] statement
 
and the [[#AssignmentAttempt|<tt>?=</tt>]] statement).
 
and the [[#AssignmentAttempt|<tt>?=</tt>]] statement).
Line 66: Line 67:
 
As we have seen previously, the left-hand side operand of the <tt>?:=</tt> type test can be some
 
As we have seen previously, the left-hand side operand of the <tt>?:=</tt> type test can be some
 
explicit type name.
 
explicit type name.
The second form of the <tt>?:=</tt> test operator is to use in place of the
+
The second form of the <tt>?:=</tt> test operator is to be used in place of the
 
explicit type, some [[Syntax diagrams#Writable|writable]] entity as in:
 
explicit type, some [[Syntax diagrams#Writable|writable]] entity as in:
 
if my_variable ?:= expression then
 
if my_variable ?:= expression then
Line 74: Line 75:
 
end
 
end
 
which evaluates to <tt>True</tt> iff the actual content of <tt>expression</tt>
 
which evaluates to <tt>True</tt> iff the actual content of <tt>expression</tt>
''could be assigned'' into <tt>my_variable</tt>.
+
''could be assigned'' to <tt>my_variable</tt>.
 
Note that the assignment is '''not''' performed.
 
Note that the assignment is '''not''' performed.
This variant avoids the need to repeat here the static type of <tt>my_variable</tt>.
+
This variant avoids the need to repeat the static type of <tt>my_variable</tt> here.
 
It is also because the <tt>?:=</tt> type test operator is often used in conjunction with
 
It is also because the <tt>?:=</tt> type test operator is often used in conjunction with
 
the [[#ForcedAssignment|<tt>::=</tt>]] forced assignment.
 
the [[#ForcedAssignment|<tt>::=</tt>]] forced assignment.
   
 
<div id="ForcedAssignment">
 
<div id="ForcedAssignment">
 
[http://www.town-china.cn &#20013;&#22830;&#21560;&#23576;][http://www.town-china.cn &#26032;&#39118;&#31995;&#32479;]
 
[http://www.town-china.cn &#26477;&#24030;&#36890;&#35802;&#25237;&#36164;&#20844;&#21496;][http://www.021boy.com &#25104;&#20154;&#29992;&#21697;]
 
[http://www.91yg.com &#25104;&#20154;&#29992;&#21697;][http://www.air520.com &#26477;&#24030;&#23130;&#24198;][http://www.anteyi.cn &#23631;&#34109;&#22120;]
 
[http://www.atetech.com.cn &#25163;&#26426;&#20449;&#21495;&#23631;&#34109;&#22120;][http://www.51wisdom.com &#32593;&#31449;&#24314;&#35774;] [http://www.51wisdom.com &#26477;&#24030;&#32593;&#32476;&#20844;&#21496;]
 
[http://www.56918.com/web.htm &#32593;&#31449;&#24314;&#35774;][http://www.56918.com &#34394;&#25311;&#20027;&#26426;][http://www.56918.com &#22495;&#21517;&#27880;&#20876;]
 
[http://www.56918.com/promote.htm &#32593;&#31449;&#25512;&#24191;][http://www.56918.com/email.htm &#20225;&#19994;&#37038;&#31665;][http://www.56918.com/domain.htm &#22495;&#21517;&#30003;&#35831;]
 
[http://www.56156.com &#29289;&#27969;][http://www.dzsc.com &#30005;&#23376;][http://www.ic37.com IC]
 
[http://www.51wisdom.com/jianshe_1.htm &#32593;&#31449;&#24314;&#35774;] [http://www.51wisdom.com/jz.htm &#34394;&#25311;&#20027;&#26426;]
 
[http://www.51wisdom.com/yinyong_1.asp &#22495;&#21517;&#27880;&#20876;] [http://www.51wisdom.com/tuiguang.asp &#32593;&#31449;&#25512;&#24191;]
 
[http://www.51wisdom.com/youju.asp &#20225;&#19994;&#37038;&#23616;][http://www.zhkaw.com &#30284;&#30151;] [http://www.51wisdom.com.cn/vi.htm vi&#35774;&#35745;] [http://www.51wisdom.com.cn/wangye &#32593;&#39029;&#35774;&#35745;] [http://www.air520.com &#26477;&#24030;&#23130;&#24198;]
 
[http://www.air520.com &#26477;&#24030;&#23130;&#24198;&#21496;&#20202;][http://www.air520.com &#26477;&#24030;&#23130;&#24198;&#20027;&#25345;]
 
[http://www.ywxjm.com &#33136;&#24102;] [http://www.51wisdom.com.cn/pingmian.htm &#24179;&#38754;&#35774;&#35745;]
 
[http://www.51wisdom.net/biaozhi.htm &#26631;&#24535;&#35774;&#35745;] [http://www.anteyi.cn &#25163;&#26426;&#20449;&#21495;&#23631;&#34109;&#22120;] [http://www.anteyi.cn &#23631;&#34109;&#22120;]
 
   
 
== The <tt>::=</tt> forced assignment ==
 
== The <tt>::=</tt> forced assignment ==
Line 112: Line 99:
 
satisfy the <tt>?:=</tt> condition, you get an error at runtime.
 
satisfy the <tt>?:=</tt> condition, you get an error at runtime.
 
In <tt>-boost</tt> mode no runtime check is performed and the runtime cost of a
 
In <tt>-boost</tt> mode no runtime check is performed and the runtime cost of a
<tt>::=</tt> statement is exactely the runtime cost of a common
+
<tt>::=</tt> statement is exactly the runtime cost of a common
 
<tt>:=</tt> statement.
 
<tt>:=</tt> statement.
 
A very low cost indeed.
 
A very low cost indeed.
Line 118: Line 105:
 
similar code using the traditional [[#AssignmentAttempt|<tt>?=</tt>]] assignment attempt.
 
similar code using the traditional [[#AssignmentAttempt|<tt>?=</tt>]] assignment attempt.
 
Besides, it helps debugging because it will raise an exception when your assumption is not
 
Besides, it helps debugging because it will raise an exception when your assumption is not
  +
correct.
satisfied.
 
   
 
As explained for the [[#AssignmentTest|<tt>?:=</tt>]] assignment test, one can not use
 
As explained for the [[#AssignmentTest|<tt>?:=</tt>]] assignment test, one can not use
Line 145: Line 132:
 
Again, there is no rush, and the traditional
 
Again, there is no rush, and the traditional
 
<tt>?=</tt> will be supported for some more years.
 
<tt>?=</tt> will be supported for some more years.
From compiler writer's point of view, the support for <tt>?=</tt> is really cheap because
+
From a compiler writer's point of view, the support for <tt>?=</tt> is really cheap because
its implementation only relies on <tt>?:=</tt> and <tt>::=</tt> support.
+
it is implemented in terms of <tt>?:=</tt> and <tt>::=</tt>.
   
 
Furthermore, it is easy and elegant to explain <tt>?=</tt> as a simple
 
Furthermore, it is easy and elegant to explain <tt>?=</tt> as a simple
Line 152: Line 139:
 
Consider the following <tt>?=</tt> usage:
 
Consider the following <tt>?=</tt> usage:
 
destination_variable ?= source_variable
 
destination_variable ?= source_variable
If the value of <tt>source_variable</tt> can not be legally assigned
+
If the value of <tt>source_variable</tt> cannot be legally assigned
into <tt>destination_variable</tt>, the <tt>destination_variable</tt> will get
+
to <tt>destination_variable</tt>, the <tt>destination_variable</tt> will be set to
 
[[Void|<tt>Void</tt>]] instead of the value of the <tt>source_variable</tt>.
 
[[Void|<tt>Void</tt>]] instead of the value of the <tt>source_variable</tt>.
 
Note that if <tt>source_variable</tt> actually contains <tt>Void</tt>,
 
Note that if <tt>source_variable</tt> actually contains <tt>Void</tt>,
Line 177: Line 164:
 
one can not use any combination of [[Glossary#StaticType|static type]] on
 
one can not use any combination of [[Glossary#StaticType|static type]] on
 
both sides of <tt>?=</tt> operator.
 
both sides of <tt>?=</tt> operator.
The very same kinds of checks are performed for the <tt>?=</tt> statement.
+
The very same kind of checks are performed for the <tt>?=</tt> statement.
 
To summarize checks, now consider:
 
To summarize checks, now consider:
 
my_variable ?= expression
 
my_variable ?= expression
Line 194: Line 181:
 
a_any := a_truck
 
a_any := a_truck
 
an_apple ?= a_any -- This is valid, APPLE conforms to ANY
 
an_apple ?= a_any -- This is valid, APPLE conforms to ANY
The example above is clearly a weird one, but you could need something like it if you have two
+
The example above is clearly a weird one, but you could need something like this if you have two
unrelated class with a common heir. And at least the code shows that you know what you are doing.
+
unrelated classes with a common heir. And at least the code shows that you know what you are doing.
This situation should arise in extremely rare situations which are not worth to be explained
+
This situation should arise in extremely rare situations only, that are not worth to be explained
 
here.
 
here.

Latest revision as of 18:45, 27 June 2016

Sometimes, you need to decide at run-time if a variable (or expression) refers to an instance of a specific class or type. Well, it is almost always better to rely on dynamic dispatch, but, in some situations, it can be necessary to explicitly test the dynamic type of some expression. While other languages provide a cast operator to do (unsafe) type casting, Liberty Eiffel provides you three operators for doing that: the ?:= assignment test operator, the ::= forced assignment statement as well as the traditional ?= assignment attempt statement.

For some examples about dynamic type testing, check also the tutorial/downcasting.e file.

Please note that assignment testing cannot be used with expanded types, for which dynamic dispatch is not applicable.

The ?:= assignment test

The assignment test ?:= is a BOOLEAN expression allowing you to check if the given right-hand side expression refers to an object which conforms to some explicitly written type. As an example, you can use the ?:= operator as follows to check that some expression actually holds an instance which could be assigned to some variable declared with SOME_TYPE:

if {SOME_TYPE} ?:= expression then
    print ("The 'expression' denotes something which can be held by the variable declared of type SOME_TYPE.")
else
    print ("Something else which is non Void.")
end

It evaluates expression and returns True if the resulting object can be legally assigned to some variable declared of type SOME_TYPE. To take now a more concrete example using existing types of our library:

if {FAST_ARRAY[INTEGER]} ?:= my_collection then
    print ("Expression 'my_collection' is either Void or conforms to FAST_ARRAY[INTEGER].")
else
    print ("Something else which is non Void.")
end

In the previous example, my_collection is declared of type COLLECTION[INTEGER]. As explained below, COLLECTION[STRING] would make the previous code invalid thus rejected by the compiler.

To avoid testing impossible situations which are probably coding mistakes, one cannot use any type for the right-hand side of a ?:= test expression. The rule is simple and naturally related to the rule used for an ordinary assignment (the common well known := assignment). Consider the following general example:

if {SOME_TYPE} ?:= some_expression then
    ...
else
    ...
end

The check performed by the compiler is that an expression of type SOME_TYPE could be legally assigned to some variable declared of the static type of some_expression. Actually, this simply means that you trust the type system and that you really want to reject invalid or, to say the least, weird code. Because of the redefinition mechanism, the validation check for the ?:= operator is only performed at the class where it appears, and is not revalidated in subtypes even if they redefine the static type of some_expression. The latest decision was driven by our experimentations. We made this decision because the strict rule would reject mostly valid situations. Similar checks are performed for all other statements of the same family (the ::= statement and the ?= statement).

As we have seen previously, the left-hand side operand of the ?:= type test can be some explicit type name. The second form of the ?:= test operator is to be used in place of the explicit type, some writable entity as in:

if my_variable ?:= expression then
   ...
else
   ...
end

which evaluates to True iff the actual content of expression could be assigned to my_variable. Note that the assignment is not performed. This variant avoids the need to repeat the static type of my_variable here. It is also because the ?:= type test operator is often used in conjunction with the ::= forced assignment.

The ::= forced assignment

The ::= forced assignment statement allows you to actually perform an assignment proved valid at runtime with a corresponding ?:= assignment test. As an example, the following code would always work without any possible runtime error:

if destination_variable ?:= source_variable then
    destination_variable ::= source_variable
else
    -- Handle this case specially...
end

Actually, the ?:= test can be considered as the precondition of the ::= part. In non -boost mode, when some ::= statement is executed but does not satisfy the ?:= condition, you get an error at runtime. In -boost mode no runtime check is performed and the runtime cost of a ::= statement is exactly the runtime cost of a common := statement. A very low cost indeed. Usage of ::= can be slightly more efficient (and result in simpler code) than similar code using the traditional ?= assignment attempt. Besides, it helps debugging because it will raise an exception when your assumption is not correct.

As explained for the ?:= assignment test, one can not use any combination of static types. The same checks are performed for the ::= statement. To summarize checks that are performed, please consider:

my_variable ::= expression

The entity my_variable must be a valid writable. The declaration type of my_variable must be a possible valid source type of some normal assignment into some destination variable declared with the static type of expression. Finally, the check in performed only where the ::= is written.

The ?= assignment attempt

The traditional assignment attempt ?= statement is still supported for historical reasons. Indeed, now that we have both the ?:= assignment test and the ::= forced assignment statement, the ?= statement is no longer necessary. In the long-term future, let's say, one or two years, usage of ?:= and ::= may become the preferred coding style. Again, there is no rush, and the traditional ?= will be supported for some more years. From a compiler writer's point of view, the support for ?= is really cheap because it is implemented in terms of ?:= and ::=.

Furthermore, it is easy and elegant to explain ?= as a simple source code transformation using only ?:= and ::=. Consider the following ?= usage:

destination_variable ?= source_variable

If the value of source_variable cannot be legally assigned to destination_variable, the destination_variable will be set to Void instead of the value of the source_variable. Note that if source_variable actually contains Void, destination_variable will always be reset with Void. Finally, the previous ?= example can be translated into the equivalent form:

if destination_variable ?:= source_variable then
   destination_variable ::= source_variable
else
   destination_variable := Void
end

Note that the previous transformation scheme may not work for all kinds of right-hand side expressions because the transformation scheme incurs two evaluations of the right-hand side part. To be valid, the previous transformation scheme assumes that the right-hand side expression incurs no side effect. Anyway, this transformation scheme is just here to explain the semantic of the ?= statement and not as a general translation scheme to patch your (not so obsolete) code.

As explained for the ?:= assignment test, one can not use any combination of static type on both sides of ?= operator. The very same kind of checks are performed for the ?= statement. To summarize checks, now consider:

my_variable ?= expression

The entity my_variable must be a valid writable. The declaration type of my_variable must be a possible source type of some normal assignment into some destination variable declared with the static type of expression. Finally, as usual the check is performed only where the ?= statement is written.

Assuming that type TRUCK and type APPLE are completely distinct types (i.e. you are not allowed to perform an assignment of TRUCK into APPLE nor of APPLE into TRUCK), the following assignment attempt is statically rejected, which is good news:

my_apple ?= my_truck -- hopefully this is statically rejected!

You can always workaround by using the following trick:

    a_any := a_truck
    an_apple ?= a_any -- This is valid, APPLE conforms to ANY

The example above is clearly a weird one, but you could need something like this if you have two unrelated classes with a common heir. And at least the code shows that you know what you are doing. This situation should arise in extremely rare situations only, that are not worth to be explained here.