There is now a subarray function designed so constant slices of arrays (of any size)
can be made to work (they don’t yet). Here’s the function:
fun subarray[FIRST:UNITSUM,LEN:UNITSUM,T,N:UNITSUM] (a:T^N)= {
typedef K =_typeop("_unitsum_min",(LEN, N `- FIRST),UNITSUM);
var o : T ^ K;
for i in ..[K] do
var first = Typing::arrayindexcount[FIRST].int;
var outix = caseno i;
var inpix = (first + outix) :>> N; // checked at run time?
&o.i <- a.inpix;
done
return o;
}
var a = 1,2,3,4,5,6,7,8;
var b = subarray[2,5] (a);
println$ b;
var c = subarray[2,10] a;
println$ c;
The code works but is not what I tried to write!
We give the types FIRST, and LEN, which must be UNITSUM kind,
to specify the first element of the subarray, and, its length. The underlying
array is type T^N, where N is also UNITSUM kind. Arrays with index of
kind UNITSUM and *linear* arrays. So this function won’t work for arbitrary
compact linear types.
With slices, its legal to run off the end, So we have to calculate the
length of the subarray by min(LEN, N - FIRST). Unfortunately, that doesn’t
work as written. The - has to be replaced by `- for symmetry with addition
which has to use `+ because + means to form a sum, not to add the types
numerically. Felix gets confused if I write min, it tries overloading and
comes up with run time min functions, instead of the type function.
So I had to spell out the type function ;(
The real problem is that I can’t specify the return type because K
is defined inside the function. I tried to do this:
fun subarray[FIRST:UNITSUM, LEN:UNITSUM, T,N:UNITSUM,
K=_typeop(“_unitsum_min”,(LEN, N `- FIRST),UNITSUM)]
(a:T^N) : T ^ K = ….
with K inside the type variable binder list. That “should” work but didn’t.
Felix is designed to allow equational constraints. The problem is it means:
[FIRST:…. T,N:UNITSUM,K with K=_typeop( … ]
where the K = _typeop … is a constraint that the LHS and RHS must unify.
In the process of unification, bound type variables have their bindings checked,
and unbound ones are bound. In this case K is unbound, so unification SHOULD
have set K but it didn’t work.
The problem is another special feature. You can write:
fun g(x:ints) => …
fun f (x:uints) => g (x);
where “ints” is a typeset consisting of all the integer types and
uints a typeset of all the unsigned integer types.
Felix checks that the constraint on x in f *implies* the constraint on x
in g, that is, it checks “all uints are ints”.
In Felix typeset is an object like:
typedef ints = typesetof (int,short,long);
which admits a membership test like
int in ints // true!
The membership test is implemented with a type match:
typematch int with
| int => 1
| short => 1
| long => 1
| _ -> 0
endmatch
where 1,0 are unit and void types respectively and are used for true
and false, respectively .. its the same rubbish hack used by C for
booleans before C had booleans. in Felix this design fault will soon
be replaced by TRUE and FALSE of kind BOOL, a compile time
boolean (but this is not done yet, its work in progress).
So .. what *constraint implication checking* does is take two typesets,
expanded into type matches and check that the LHS pattern of every case,
one by one, matches some pattern of the RHS. If every pattern matches,
then, any type which satisfies the first type match must also satisfy the second,
in other words, the first typeset is a subset of the second one, in other
words (again) the first typeset, considered as a constraint on a type,
*implies* the second one.
Constraint implication checking is essential, otherwise, the call to g(x)
inside f must be bombed out because the constraint that the argument
to g is an integer must be failed with an arbitrary type variable. In particular
the short hand is expanded to
fun g[T with T in ints] (x:T) => …
which is expanded to
fun g[T with typematch T with | int => 1 | short => 1 | long => 1 | _ => 0 endmatch] (x:T) …
The PROBLEM is that constraint implication in Felix at the moment ONLY works for
typeset constraints.
It doesn’t work for equational constraints. In fact, this doesn’t matter!
The purpose of the equation constraints is two fold:
* first, it can bomb out a call where the equations aren’t satisfied.
In this case, propagation of the constraint to an inner call by implication
works just fine with no additional code. If the outer constraint fails,
the inner constraint is irrelevant which satisfies implication.
* second, if the outer constraint succeeds, then the inner one may succeed or fail,
but it is just another equation on the outer type variables: it either
unifies or doesn’t. The constraint is *propagated* by substittution.
For example if you have
fun g[U,V with P (U,V)]
where P is some constraint then if you have
fun f[A,B] with Q[A,B] ( … ) => g[A * B, A + B] …
then the inner constraint checked is
P (A * B, A + B)
in other words the constraint of g *specialised* by replacing
the constraints parameters U,V with the arguments A * B, A + B.
“We don’t need no type set implication here”.
Actually we can view all constraints as typesets, which happens
to be one element type sets. Unfortunately Felix is NOT doing that,
it bombs out equational constraints because they’re not explicitly
typeset constraints.
This problem has to be fixed .. eventually. I’m not sure what typesets are.
From one point of view a typeset is a noun which is just a convenience for
specifying a predicate: a set is just sugar making it easy to construct
membership predicates. That is
x in {a,b,c}
is just shorthand for
x == a or x == b or x == c
Sets dont exist, only logic exists, sets are just a sugar to make logic easier
to write.
HOWEVER .. at present typesets constraints just say “yes” or “no”.
The type is in the set or not.
Equational constraints do more than say yes or no. They can also
SET the value of a variable.
And that is indeed the intent for K. In this form:
[FIRST:…. T,N:UNITSUM,K with K=_typeop( … ]
I am not trying to add a constraint on K at all, I’m trying to DEFINE K
in terms of the other type variables.
This COULD be written as a typeset, provided we allow polymorphic elements
in typesets.
In some sense a type set is a (non-discriminated) union type.
That’s my confusion at the moment. In one sense, a typeset is a
subkind of a constraint kind, more precisely a noun kind, which can
be used to construct function returning BOOL, i.e. constraint predicates.
In another sense, it is an actual type constructor. You can look it up
in Wikipedia. They’re a bit arcane but union and intersection types
exist with the “obvious” meaning: a union or two types A and B is just
a type which is in A or in B, an intersection is just a type in both.
I have to decide on which model to use. Anyhow that’s the status:
the hackery in subarray function is enough to implement constant slices
of BIG arrays. (Small slices are implemented like tuples, by listing
the elements inside the compiler .. not a good idea for arrays
of 100,000 elements .. :)
—
John Skaller
ska...@internode.on.net