Sharing a type with a generic class? - generic-programming

How do you declare a variable to be the same type as a type parameter used to instantiate a generic class? The following code does not compile:
class
TEST [G, H -> INTEGER]
feature
f (i: INDEXABLE [G, H])
local
y: H
do
y := i.lower -- Type error here.
end
end
The compiler says that the source of the assignment is not compatible with target.

In the current implementation, INDEXABLE [G, H] inherits from TABLE [G, INTEGER]. As a result, lower is of type INTEGER, not H. And INTEGER does not conform to the formal generic type H of the class TEST. This explains the error.
To me, it looks like a mistake in the declaration of class INDEXABLE. It should inherit from TABLE [G, H] instead. Then, the example code would compile.

Type anchoring can be used in those cases:
feature
f (i: INDEXABLE [G, H])
local
y: like i.lower
do
y := i.lower
end
Sometimes a generic type is not used as return type of any accessible feature on a class, so in those cases I like to declare a fake feature specifically to allow anchoring:
class SOME_CLASS [G]
feature
generic_type_anchor: G
do
check
for_anchoring_only: False
-- This method should never actually be called, only used as an anchor in type declarations
end
end
This is particularly useful with complex inheritance trees or when descendant classes close the generics, in which case the correct type is not apparent from the declared type. Personally I tend to use type anchoring whenever values are semantically related as this helps expressing intent, simplifies refactoring (as there are fewer repetitions of types which by definition must match) and facilitates covariance.
Also as a side-note, expanded types (like INTEGER) cannot be used polymorphically (you need a reference for that; if class A is expanded and class B [expanded or reference] inherits A, you could not assign a value of type B to a variable of type A; inheritance from expanded types is implicitly non-conforming), and moreover the compiler disallows inheriting from basic expanded types (INTEGER, BOOLEAN, REAL_64, etc.), therefore the generic constraint in your example makes no sense because H could never be anything but INTEGER.

Related

Why does generic TArray Create syntax differs from other class functions?

I've noticed something that appears to me as an inconsistency in the generic TArray syntax (and drives me crazy...)
The "constructor" function requires to be called by specifying the type before the function name.
MyArray := TArray<Integer>.Create(3, 2, 1);
The other class functions requires to be called by specifying the type after the function name
TArray.Sort<Integer>(MyArray);
Is there a pratical reason why they did that?
The generic array is defined like this
TArray<T> = array of T;
This an alias for a dynamic array. Dynamic arrays have an intrinsic Create method. Even non-generic ones.
type
TMyDynArr = array of Integer;
....
arr := TMyDynArr.Create(0, 1, 2);
This Create method is a compiler intrinsic. Notice how it accepts arbitrary numbers of arguments.
On the other hand there is the class TArray, with its generic Sort method. This is implemented in the RTL. Remember that TArray is never instantiated, it's just a home for generic class methods.
My guess is that adding methods to the dynamic array type requires compiler support because they are intrinsic functions. But adding to TArray is simpler because it is done at the RTL layer, not requiring compiler support.
There's nothing you can do to change anything here, so there is little to be gained by fretting about this. You can't change it. Just get used to it.
The first TArray is a system type definition of array of T. The creation could be written this way as well:
MyArray := [3,2,1];
The second TArray is a class defined in Generics.Collections.
They have nothing to do with each other.
Note also that the TArray class way of using generics is called Parameterized Methods.
Type
TArray = class
...
class procedure Sort<T>(var Values: array of T); overload; static;
...
end;
That is a way to reduce code duplication.

Eiffel, multiple types conformance: a way to specify that a parameter is a descendent from A and B?

Is there a way (I'm sure there is out of runtime check...) to specify that a parameter or a variable in general conforms to multiple types? to avoid doing something such as
work (a_printer: PRINTER; a_scanner: SCANNER)
do
a_printer.print
a_scanner.scan
-- OR without second parameter
if attached {SCANNER} a_printer as l_scanner then
l_scanner.scan
else
throw RuntimeError
end
end
If feature work belongs to a class that may have formal generic parameters, it could be defined as taking one argument of the corresponding formal generic type:
class X [D -> {PRINTER, SCANNER}] feature
work (device: D)
do
device.scan
device.print
end
end
Then, at the caller site, one could make the call
x.work (multi_function_device)
where x has an appropriate type, e.g. X [MULTI_FUNCTION_PRINTER].
If work could also be declared and implemented as a class feature, the temporary variable could be avoided:
{X [like multi_function_device]}.work (multi_function_device)
If the auxiliary class X is not an option, the current version of the language provides no means to declare an argument as conforming to more than 1 type (e.g., work (d: {PRINTER, SCANNER})), so you would have to resort to preconditions like
work (p: PRINTER)
require
attached {SCANNER} p
do
check
from_precondition: attached {SCANNER} p as s
then
s.scan
end
p.print
end
I think that, if possible, you should use a common ancestor to your multiple types. If you cannot (if you are using library types), you can create descendant classes (MY_PRINTER inherit from PRINTER and DEVICE and MY_SCANNER inherit from SCANNER and DEVICE). Another way is to use ANY as the type, but it is not the best solution.

Generic Numeric Arrays in Julia

I'm trying to learn Julia by reading the documentation, and they have code that looks like this:
function testFunction(x::Number)
return x+5
end
This is a function that works for any of the many numeric types in Juila.
However, if I attempt to do something similar, like this:
function testFunction2(x::Array{Number})
return x
end
I get the following error:
ERROR: MethodError: no method matching testFunction2(::Array{Int64,1})
Closest candidates are:
testFunction2(::Array{Number,N} where N) at /Users/.../Desktop/Test.jl:45
Am I doing something wrong? I thought this: Array{Float64} is how you declare an array of a specific type, yet using a type like Number, which works for the regular case, doesn't work here... Any insight is appreciated.
The definition
function testFunction2(x::Array{Number})
return x
end
is the identify function that accepts as input an Array{Number}. So the following will work:
testFunction2(collect((1,3,-2.7,5+2im)))
but this will not work:
testFunction2([1,3,7,9])
This can be seen from here:
julia> typeof(collect((1,3,-2.7,5+2im)))
Array{Number,1}
julia> typeof([1,3,7,9])
Array{Int64,1}
The first matches the type of x in your definition of testFunction2 and the second does not. (Note that Array{Number} is synonymous for Array{Number,1}.)
What you are looking for is a function that accepts an array of any element type which is a subtype of Number. This is done as:
function testFunction2(x::Array{T}) where {T<:Number}
return x
end
where now we have a parameter T that is allowed to be any subtype of Number. You also get to use the sugar
function testFunction2(x::Array{<:Number})
return x
end
or even testFunction2(x::Array{<:Number}) = x.
Adding to the other answer: the reason there is a difference in behaviour between the example in the manual and your code is that in your case, Number is used as the type parameter in another type (Array). That gets stricter type checks than if Number was used as the type by itself. As #StefanKarpinski says in this answer,
This is a consequence of parametric type invariance in Julia. See the chapter on types in the manual for more details.
(Link corrected to be current.)
The key point from the manual is: "even though Float64 <: Real we DO NOT have Point{Float64} <: Point{Real}." Similarly in your case, even though we have Int64 <: Number, we do not have Array{Int64} <: Array{Number}, which is why the type matching fails.
So in general, you can write x::Number to match any number type, you can write p::AbstractString to match any string type that's a subtype of AbstractString, etc. But if you're using the abstract type as a type parameter as part of another type, then you have to specify explicitly that you want it to match all subtypes of the parameter type i.e. with Point{<:Real} or Array{<:Number}.

Grammar symbol attributes implementation in C

I need to implement compiler (lexical, syntax and semantic analyzers).
I have already implemented the lexical from flex++, syntax from hand (recursive descent),
and know I'm working on semantic (adding semantic rules to the existing parser).
My question is - how do I implement attributes.
For example, for production:
TYPE -> integer
I need to add semantic rule:
TYPE.type = integer
So here I need to create some structure for TYPE, and so on for all the grammar symbols.
So how do I implement that structures? Do I need to make some struct for each grammar symbol and it need to be global for scope of file?
p.s. maybe it is not the best compiler implementation, it is just requirment for assignment.
It is easiest if you define a type (typically a struct) for each terminal and nonterminal in your language. Then each use of a (non)terminal has access to the type, and each assignment of an attribute goes against the corresponding slot in the structure associated with (non)terminal.
I suggest you name your implementation structures after the grammar tokens. So, the type of the attribute structure for T would be "T_attributes { ... } "
For your example, "type" would be a slot in the T_attributes. Probably what you meant to write in the abstract was:
TYPE -> 'integer' ; -- 'integer is a keyword'
TYPE.type = "int"; -- the type of an 'integer' is "int"
Given you are doing this by hand, you will hand-compile the attribute assignment to execute when a tree walk hits the TYPE node.

Haskell Converting from Array to Unboxed Array breaks rewrite rules

I am trying to convert my program from using Data.Array to Data.Array.Unboxed.
As a quick side note:
several places state that I can change "Array" to "UArray" in my code
and ADD an import of Data.Array.Unboxed, however I am not mixing both
types of arrays so
I just imported Data.Array.Unboxed instead of Data.Array, is this sufficient?
When I make the switch the following rewrite rule breaks:
{-# RULES
"applyWindow/applyWindow" forall win1 win2 image.
applyWindow win1
(applyWindow win2
image) =
applyWindow (indexMult win1 win2)
image
#-}
Here win1 win2 and image should all be UArrays. However, this fails to compile with the follwing errors.
FIPlib/Core.hs:229:99:
Ambiguous type variables `e0', `a0' in the constraint:
(IArray a0 e0) arising from a use of `applyWindow'
Probable fix: add a type signature that fixes these type variable(s)
In the expression: applyWindow (indexMult win1 win2) image
When checking the transformation rule "applyWindow/applyWindow"
FIPlib/Core.hs:229:99:
Ambiguous type variables `e0', `a2' in the constraint:
(IArray a2 e0) arising from a use of `applyWindow'
Probable fix: add a type signature that fixes these type variable(s)
In the expression: applyWindow (indexMult win1 win2) image
When checking the transformation rule "applyWindow/applyWindow"
FIPlib/Core.hs:229:112:
Ambiguous type variables `e0', `a1' in the constraint:
(IArray a1 e0) arising from a use of `indexMult'
Probable fix: add a type signature that fixes these type variable(s)
In the first argument of `applyWindow', namely
`(indexMult win1 win2)'
In the expression: applyWindow (indexMult win1 win2) image
When checking the transformation rule "applyWindow/applyWindow"
What makes this ambiguous? Why does this break when it works with Data.Array?
The problem is that Data.Array.Unboxed reexports Data.Array.IArray, and hence the IArray class for the immutable-array interface, but Data.Array doesn't (it doesn't even import it). So if you use functions like bounds, accumArray, array etc. with only Data.Array imported, they are monomorphic in the array type, thus no ambiguity arises (the polymorphism in the element type apparently doesn't pose a problem here, it would with suitable type class constraints). But if you import Data.Array.Unboxed, all these functions get an IArray a e constraint, and thus the rewriting might involve different array types, thus breaks. You need to fix the types with type signatures, on the functions or int the rule. How exactly to solve this I cannot say without seeing the code.
Note: Type checking has changed with 7.4, with ghc-7.4.1, the code without the rule doesn't compile without type signatures if importing Data.Array.Unboxed.
To fix the types, so that the rule doesn't run into ambiguity, you have to give type signatures
on the top-level, to applyWindow,
to the two local bindings of paddedImage and filteredPaddedImage.
The probably desired and most reasonable is for all involved arrays to have the same type, that can be
a monmorphic type, say UArray (Int,Int) Int (element type could also be Word, ...)
a type monomorphic in the array type and polymorphic in the element type
a type ploymorphic in both
For 1., you simply have to add the signatures, applyWindow :: T -> T -> T and xxImage :: T (where T is the desired type). For 2., you have to add two language extensions, FlexibleContexts and ScopedTypeVariables, then applyWindow would get the signature
applyWindow :: forall e. (Num e, IArray UArray e)
=> UArray (Int,Int) e -> UArray (Int,Int) e -> UArray (Int,Int) e
and the local bindings would get the signature xxImage :: UArray (Int,Int) e. FlexibleContexts is needed to allow the occurrence of UArray in the constarint, and ScopedTypeVariables is necessary to bring the element type into scope so that paddedImage and filteredPaddedImage can get type signatures at all. For 3., only ScopedTypeVariables is needed, the type signature of applyWindow would then be
applyWindow :: forall a e. (Num e, IArray a e) => ...
Another method to force all arrays to the same type is using asTypeOf or putting them all into a list (unused local binding), but IMO type signatures are preferable.
Once all types are fixed (they need not necessarily be the same, but the type of the local bindings must be determined by the argument and result types, possibly the argument types alone), the rule should type check. (It may be necessary to fix types in indexMult too.)
I've received exactly same error message (about IArray etc.) when I tried running this code without the explicit type signature for the array (UArray Int Bool in that case), just with the Data.Array.Unboxed import. When I added the signature all was OK. Maybe this will help you too.

Resources