Difference between revisions of "Language Reference/Conversion"

From wiki.visual-prolog.com
m (link)
 
(5 intermediate revisions by 2 users not shown)
Line 1: Line 1:
{{languageReferenceNavbar|Conversion}}
{{languageReferenceNavbar|Conversion}}


== View and Construction Types ==
=== View and Construction Types ===


An interface defines a type of objects, all objects that supports the interface have that type. In the sequel we shall use the term ''the object type'' as synonym for the term ''the type defined by the interface''.
An interface defines a type of objects, all objects that supports the interface have that type. In the sequel we shall use the term ''the object type'' as synonym for the term ''the type defined by the interface''.
Line 11: Line 11:
So the same object can be viewed as having different types in different contexts. The type with which an object is viewed is called its '''''view type''''', whereas the type of the class that constructed the object is called the '''''construction type''''' or ''definition type''. The ''construction type'' is also a view type.
So the same object can be viewed as having different types in different contexts. The type with which an object is viewed is called its '''''view type''''', whereas the type of the class that constructed the object is called the '''''construction type''''' or ''definition type''. The ''construction type'' is also a view type.


=== Type Conversion ===
==== Type Conversion ====


As mentioned above objects can be used as having the type of any supported interface. This section describes how conversions between various supported types is handled.
As mentioned above objects can be used as having the type of any supported interface. This section describes how conversions between various supported types is handled.


==== Conversion Upwards ====
===== Conversion Upwards =====


If some term is statically typed to some type <vp>T1</vp> and <vp>T1</vp> is declared to support <vp>T2</vp> then it is obvious that any object referenced by the variable will indeed support <vp>T2</vp>. So upward support information is statically known. Subsequently all conversions upwards in the support hierarchy is performed automatically.
If some term is statically typed to some type <vp>T1</vp> and <vp>T1</vp> is declared to support <vp>T2</vp> then it is obvious that any object referenced by the variable will indeed support <vp>T2</vp>. So upward support information is statically known. Subsequently all conversions upwards in the support hierarchy is performed automatically.


'''Example'''
{{Example| Assume the existence of the interface <vp>bb</vp> which supports an interface <vp>aa</vp> and the class <vp>bb_class</vp> with construction type <vp>bb</vp>. Consider the following code:
 
Assume the existence of the interface <vp>bb</vp> which supports an interface <vp>aa</vp> and the class <vp>bb_class</vp> with construction type <vp>bb</vp>. Consider the following code:


<vip>implement ...
<vip>implement ...
Line 37: Line 35:


In the line marked (1) we create a <vp>bb_class</vp> object: the object has construction type <vp>bb</vp>. The variable <vp>BB</vp> is a reference to this new object. <vp>BB</vp> provides the view type <vp>bb</vp> on the object. In the line marked (2) the object is passed to <vp>ppp</vp> as an <vp>aa</vp> object. The conversion from view type <vp>bb</vp> to view type <vp>aa</vp> is performed implicitly. When the object reaches the line marked (3) it has view-type <vp>aa</vp>, though the construction type is still <vp>bb</vp>.
In the line marked (1) we create a <vp>bb_class</vp> object: the object has construction type <vp>bb</vp>. The variable <vp>BB</vp> is a reference to this new object. <vp>BB</vp> provides the view type <vp>bb</vp> on the object. In the line marked (2) the object is passed to <vp>ppp</vp> as an <vp>aa</vp> object. The conversion from view type <vp>bb</vp> to view type <vp>aa</vp> is performed implicitly. When the object reaches the line marked (3) it has view-type <vp>aa</vp>, though the construction type is still <vp>bb</vp>.
}}


== Explicit Conversion ==
=== Explicit Conversion ===


Explicit conversion is performed by calling a conversion predicate.
Explicit conversion is performed by calling a conversion predicate.
Line 44: Line 43:
Several conversion predicates are available.
Several conversion predicates are available.


=== Checked Conversion ===
==== Checked Conversion ====


The predicates {{lang2|Built-in_entities|convert/2->|convert}}/2-> and {{lang2|Built-in_entities|tryConvert/2->|tryConvert}}/2-> are used to perform safe conversion from one type to another type.
The predicates {{lang2|Built-in_entities|convert|convert/2->}} and {{lang2|Built-in_entities|tryConvert|tryConvert/2->}} are used to perform safe conversion from one type to another type.


Neither predicate can be given a real declaration, but here are pseudo declarations for them
Neither predicate can be given a real declaration, but here are pseudo declarations for them
Line 54: Line 53:
   tryConvert : ("type" Type, _ Value) -> Type ConvertedValue determ.</vip>
   tryConvert : ("type" Type, _ Value) -> Type ConvertedValue determ.</vip>


Both {{lang2|Built-in_entities|convert/2->|convert}}/2-> and {{lang2|Built-in_entities|tryConvert/2->|tryConvert}}/2-> take a "type" as the first argument and a value of any type as the second argument and will then return the converted value as the result.
Both {{lang2|Built-in_entities|convert|convert/2->}} and {{lang2|Built-in_entities|tryConvert|tryConvert/2->}} take a "type" as the first argument and a value of any type as the second argument and will then return the converted value as the result.


{{lang2|Built-in_entities|convert/2->|convert}}/2-> will raise an exception if the conversion is impossible, while {{lang2|Built-in_entities|convert/2->|convert}}/2-> and {{lang2|Built-in_entities|tryConvert/2->|tryConvert}}/2-> simply fails in that case.
{{lang2|Built-in_entities|convert|convert/2->}} will raise an exception if the conversion is impossible, while {{lang2|Built-in_entities|tryConvert|tryConvert/2->}} simply fails in that case.


Notice that the use of {{lang2|Built-in_entities|convert/2->|convert}}/2-> and {{lang2|Built-in_entities|tryConvert/2->|tryConvert}}/2-> is always superfluous, if the source type is a subtype of the target type, because then the conversion will be performed implicitly.
Notice that the use of {{lang2|Built-in_entities|convert|convert/2->}} and {{lang2|Built-in_entities|tryConvert|tryConvert/2->}} is always superfluous, if the source type is a subtype of the target type, because then the conversion will be performed implicitly.


{{lang2|Built-in_entities|convert/2->|convert}}/2-> and {{lang2|Built-in_entities|tryConvert/2->|tryConvert}}/2-> can be used in the following situations:
{{lang2|Built-in_entities|convert|convert/2->}} and {{lang2|Built-in_entities|tryConvert|tryConvert/2->}} can be used in the following situations:


*converting from one number domain to another number domain
*converting from one number domain to another number domain
Line 67: Line 66:
The compiler may complain (but does not have to) if it can determine that a conversion can never succeed, for example if attempting to convert between number domains that does not have overlapping ranges.
The compiler may complain (but does not have to) if it can determine that a conversion can never succeed, for example if attempting to convert between number domains that does not have overlapping ranges.


=== Conversion Downwards ===
==== Conversion Downwards ====


When an object is converted to a super type (i.e. to a supported interface), then information about the object is "forgotten". Notice that the capabilities are not really lost they are just not visible in the context where the object is seen with a less capable interface.
When an object is converted to a super type (i.e. to a supported interface), then information about the object is "forgotten". Notice that the capabilities are not really lost they are just not visible in the context where the object is seen with a less capable interface.
Line 75: Line 74:
Downward conversion cannot (in general) be validated statically. Therefore, it is necessary to use explicit conversion when restoring "lost" interfaces.
Downward conversion cannot (in general) be validated statically. Therefore, it is necessary to use explicit conversion when restoring "lost" interfaces.


'''Example'''
{{Example|
 
While it is extremely simple to make sensible illustration of type conversions up in the supports-hierarchy, it requires a "real" example to illustrate sensible use of downward conversion. Therefore we shall present a more "real" example here.
While it is extremely simple to make sensible illustration of type conversions up in the supports-hierarchy, it requires a "real" example to illustrate sensible use of downward conversion. Therefore we shall present a more "real" example here.


Line 85: Line 83:
<vip>interface objectSet
<vip>interface objectSet
   predicates
   predicates
       insert : (object Elem) procedure (i)
       insert : (object Elem).
       getSomeElem : () -> object determ (i)
       getSomeElem : () -> object determ.
   ...  
   ...  
end interface
end interface
Line 96: Line 94:
<vip>interface myObjectSet
<vip>interface myObjectSet
   predicates
   predicates
       insert : (myObject Elem) procedure (i).
       insert : (myObject Elem).
       getSomeElem : () -> myObject determ ().
       getSomeElem : () -> myObject determ.
   ...  
   ...  
end interface
end interface
Line 119: Line 117:
end implement</vip>
end implement</vip>


In the line marked (1) <span class=vp-variable>Elem</span> is automatically converted from type <vp>myObject</vp> to <vp>object</vp>. In the line marked (2) we retrieve an object from the embedded <vp>object</vp> set. Technically this object has type <vp>object</vp>. But from our invariant we know that the object also supports <vp>myObject</vp>. Subsequently, we know that we can safely restore the <vp>myObject</vp> interface. This is explicitly done in the line marked (3).
In the line marked (1) <vp>Elem</vp> is automatically converted from type <vp>myObject</vp> to <vp>object</vp>. In the line marked (2) we retrieve an object from the embedded <vp>object</vp> set. Technically this object has type <vp>object</vp>. But from our invariant we know that the object also supports <vp>myObject</vp>. Subsequently, we know that we can safely restore the <vp>myObject</vp> interface. This is explicitly done in the line marked (3).
}}


=== Private and Public Types ===
==== Private and Public Types ====


When an object is created with a constructor it is returned with the construction type. Such an object can automatically be converted to any supported interface and explicitly back again.  
When an object is created with a constructor it is returned with the construction type. Such an object can automatically be converted to any supported interface and explicitly back again.  
Line 133: Line 132:
So an object have two views the public view and the private view. The private view includes the public type. The object cannot be converted from one view to another, but since the private view includes the public type, the private view can be converted to any supported type whatsoever.
So an object have two views the public view and the private view. The private view includes the public type. The object cannot be converted from one view to another, but since the private view includes the public type, the private view can be converted to any supported type whatsoever.


=== Unchecked Conversion ===
==== Unchecked Conversion ====


The predicate {{lang2|Built-in_entities|uncheckedConvert/2->|uncheckedConvert/2}} is used to perform unsafe conversions based on memory representation. The predicate does not modify memory in any way, it simply forces the compiler to interpret that piece of storage with another type.
The predicate {{lang2|Built-in_entities|uncheckedConvert|uncheckedConvert/2->}} is used to perform unsafe conversions based on memory representation. The predicate does not modify memory in any way, it simply forces the compiler to interpret that piece of storage with another type.


'''Notice''' this predicate is highly unsafe and should be used with maximum precautions.
'''Notice''' this predicate is highly unsafe and should be used with maximum precautions.
Line 141: Line 140:
The predicate is intended to be used when interfacing to foreign languages, in order to interpret the memory images these foreign languages uses.
The predicate is intended to be used when interfacing to foreign languages, in order to interpret the memory images these foreign languages uses.


{{lang2|Built-in_entities|uncheckedConvert/2->|uncheckedConvert/2}} can only be used on pieces of memory that have exactly the same bit-size. However many kinds of data are represented by a pointer and such data have the same bit-size.
{{lang2|Built-in_entities|uncheckedConvert|uncheckedConvert/2->}} can only be used on pieces of memory that have exactly the same bit-size. However many kinds of data are represented by a pointer and such data have the same bit-size.


<vip>predicates
<vip>predicates
   uncheckedConvert : ("type" Type, _ Value) -> Type ConvertedValue.</vip>
   uncheckedConvert : ("type" Type, _ Value) -> Type ConvertedValue.</vip>


'''Example'''
{{Example|
 
<vip>predicates
<vip>predicates
   interpretBufferAsString : (pointer BufferPointer) -> string Value.
   interpretBufferAsString : (pointer BufferPointer) -> string Value.
Line 156: Line 154:


This is only sound if the memory block has the correct representation to be a string.
This is only sound if the memory block has the correct representation to be a string.
}}

Latest revision as of 10:49, 26 October 2012

View and Construction Types

An interface defines a type of objects, all objects that supports the interface have that type. In the sequel we shall use the term the object type as synonym for the term the type defined by the interface.

Since interfaces define types, these types can be used as formal type specifiers in predicate and fact declarations and in domain definitions.

Since an object have the object type of any interface it supports, it can be used as an object of any of these types. I.e. the type of objects is converted to any supported object type. As described below the conversion is in many cases performed automatically.

So the same object can be viewed as having different types in different contexts. The type with which an object is viewed is called its view type, whereas the type of the class that constructed the object is called the construction type or definition type. The construction type is also a view type.

Type Conversion

As mentioned above objects can be used as having the type of any supported interface. This section describes how conversions between various supported types is handled.

Conversion Upwards

If some term is statically typed to some type T1 and T1 is declared to support T2 then it is obvious that any object referenced by the variable will indeed support T2. So upward support information is statically known. Subsequently all conversions upwards in the support hierarchy is performed automatically.

Example Assume the existence of the interface bb which supports an interface aa and the class bb_class with construction type bb. Consider the following code:
implement ...
   predicates
       ppp : (aa AA).
   clauses
       ... :-
           BB = bb_class::new(), % (1)
         ppp(B). % (2)
   clauses
       ppp(AA) :- % (3)
           ...
           BB = convert(bb,AA),    % Conversion possible since definition type of AA is bb
           ...

In the line marked (1) we create a bb_class object: the object has construction type bb. The variable BB is a reference to this new object. BB provides the view type bb on the object. In the line marked (2) the object is passed to ppp as an aa object. The conversion from view type bb to view type aa is performed implicitly. When the object reaches the line marked (3) it has view-type aa, though the construction type is still bb.

Explicit Conversion

Explicit conversion is performed by calling a conversion predicate.

Several conversion predicates are available.

Checked Conversion

The predicates convert/2-> and tryConvert/2-> are used to perform safe conversion from one type to another type.

Neither predicate can be given a real declaration, but here are pseudo declarations for them

predicates
   convert : ("type" Type, _ Value) -> Type ConvertedValue.
   tryConvert : ("type" Type, _ Value) -> Type ConvertedValue determ.

Both convert/2-> and tryConvert/2-> take a "type" as the first argument and a value of any type as the second argument and will then return the converted value as the result.

convert/2-> will raise an exception if the conversion is impossible, while tryConvert/2-> simply fails in that case.

Notice that the use of convert/2-> and tryConvert/2-> is always superfluous, if the source type is a subtype of the target type, because then the conversion will be performed implicitly.

convert/2-> and tryConvert/2-> can be used in the following situations:

  • converting from one number domain to another number domain
  • converting an object to another type

The compiler may complain (but does not have to) if it can determine that a conversion can never succeed, for example if attempting to convert between number domains that does not have overlapping ranges.

Conversion Downwards

When an object is converted to a super type (i.e. to a supported interface), then information about the object is "forgotten". Notice that the capabilities are not really lost they are just not visible in the context where the object is seen with a less capable interface.

In many situations it is necessary to restore the actual capabilities of the objects. Therefore, we need to be able to convert them downward as well as upwards.

Downward conversion cannot (in general) be validated statically. Therefore, it is necessary to use explicit conversion when restoring "lost" interfaces.

Example

While it is extremely simple to make sensible illustration of type conversions up in the supports-hierarchy, it requires a "real" example to illustrate sensible use of downward conversion. Therefore we shall present a more "real" example here.

Assume that we want to implement "sets of homogeneously typed objects". I.e. sets of objects which all supports a certain view type. We know that we will need such sets for several types of objects. Therefore we want to make our implementation in a way, which can easily be adopted to many different types of objects, but yet preserve the homogeneity of the contained objects.

Our approach is fairly standard: we make the actual implementation of the "set" based on the object type, which any object supports. And then construct the more specific versions of "sets" by means of a thin layer, which will convert between the actual type and object. We shall not show the actual implementation of object sets, we shall merely assume that it exists in the shape of the following class and interface:

interface objectSet
   predicates
       insert : (object Elem).
       getSomeElem : () -> object determ.
   ... 
end interface
class objectSet_class : objectSet
end class

Now assume that we have some object type myObject and that we want to create the corresponding "set" class myObjectSet_class. We declare myObjectSet_class as following:

interface myObjectSet
   predicates
       insert : (myObject Elem).
       getSomeElem : () -> myObject determ.
   ... 
end interface
class myObjectSet_class : myObjectSet
end class

I.e. myObjectSet has all the predicates of objectSet but every occurrence of object is replaced with myObject. The implementation of myObjectSet_class inherits from objectSet_class, this embedded/inherited objectSet will carry the members of the set. The implementation will fulfill the following invariant: The embedded objectSet will only contain objects of type myObject (even though they "technically" have type object).

The implementation looks as follows:

implement myObjectSet_class
    inherit objectSet_class
   clauses
       insert(Elem) :-
           objectSet_class::insert(Elem). % (1)
       getSomeElem() = Some :-
           SomeObject = objectSet_class::getSomeElem(), % (2)
           Some = convert(myObject, SomeObject). % (3)
   ...
end implement

In the line marked (1) Elem is automatically converted from type myObject to object. In the line marked (2) we retrieve an object from the embedded object set. Technically this object has type object. But from our invariant we know that the object also supports myObject. Subsequently, we know that we can safely restore the myObject interface. This is explicitly done in the line marked (3).

Private and Public Types

When an object is created with a constructor it is returned with the construction type. Such an object can automatically be converted to any supported interface and explicitly back again.

Even if the class that implements the object have stated further supported interfaces privately it is impossible to convert the "public" object to any of these private types.

In the implementation however the object can be accessed with any privately supported type. Furthermore "This" can be handed outside the implementation with any of these privately supported types.

Such a "private" version of an object can also be converted implicitly upwards in its hierarchy and explicitly downwards again. In fact such a "private" object can be converted explicitly to any publicly or privately supported interface.

So an object have two views the public view and the private view. The private view includes the public type. The object cannot be converted from one view to another, but since the private view includes the public type, the private view can be converted to any supported type whatsoever.

Unchecked Conversion

The predicate uncheckedConvert/2-> is used to perform unsafe conversions based on memory representation. The predicate does not modify memory in any way, it simply forces the compiler to interpret that piece of storage with another type.

Notice this predicate is highly unsafe and should be used with maximum precautions.

The predicate is intended to be used when interfacing to foreign languages, in order to interpret the memory images these foreign languages uses.

uncheckedConvert/2-> can only be used on pieces of memory that have exactly the same bit-size. However many kinds of data are represented by a pointer and such data have the same bit-size.

predicates
   uncheckedConvert : ("type" Type, _ Value) -> Type ConvertedValue.
Example
predicates
   interpretBufferAsString : (pointer BufferPointer) -> string Value.
clauses
   interpretBufferAsString(BufferPointer) = uncheckedConvert(string, BufferPointer).

This predicate will interpret (convert) a buffer represented by a pointer as a string.

This is only sound if the memory block has the correct representation to be a string.