Ellipsis type

1 view
Skip to first unread message

John Skaller2

unread,
Feb 1, 2020, 9:58:21 PM2/1/20
to felix google
I have now added the capability to make this work:

proc printf: +char * ... = "printf($a);";
printf (c"%d %f %d\n",1,2.2,3);

The current code is a bit hacky to say the least :-)

Rationale: lots of C APIs use variable argument lists.
Not being able to bind them is a pain.


Intended Semantics.
================

This is a form of row polymorphism which is isomorphic to replacing
the ellipsis with a type variable, except that the replacement for
the type variable is a tuple which is expanded into its components.

A type of the form

T1 * T2 * T3 * …

may only be used as the parameter of a C function or procedure.
The function or procedure will then accept a tuple argument
whose leading components match T1, T2, T3, and may have zero,
one, or more components. The whole argument tuple is passed to
the C function.


Bugs and todos:

* This is only implemented for tuples. It doesn’t work for arrays yet.
For example this fails:

printf (c"%s %s %s\n",c"1",c"2",c"3”);

because the argument type is an array. Will be fixed shortly.

* Felix currently doesn’t type check the specified argument
in all places it should. Basically if the parameter uses an ellipsis,
any argument might work. However the unification engine DOES
make the correct checks (but also only matches tuples that aren’t arrays).

Note this form is NOT the same as varargs. As in C, varargs is a way
in a function with an ellipsis tailed parameter can capture and pass the
trailing arguments to a function using varargs.

* It isn’t supported for Felix functions.
But it might work sometimes and could be given semantics.
There is no code in the compiler that restricts anything to C functions.

An idea would simply be that a subtyping coercion is applied
which strips off the trailing components of the argument,
since there’s no name to refer to those components.

However in C, there is: varargs macros can be used to capture
the actual tuple.

In Felix, the form

T1 <**> T2

and expression

head,,tail

allows a tuple to be processed with polymorphic recursion in type classes.
This is a form of row polymorphism for tuples, which is in fact stronger
than the record form. So we could decide that using

T1 * …

actually means

T1 <**> T2

except for the problem that this polymorphic recursion requires T2 to be a tuple.

I’ll be happy at the moment if it works for C bindings.




John Skaller
ska...@internode.on.net





John Skaller2

unread,
Feb 1, 2020, 10:33:12 PM2/1/20
to felix google
Ok this now works. Not quite sure why, i made some changes to the
compiler but not as many as I though should be needed.

fun printf: +char * ... -> int = "printf($a)";
C_hack::ignore$ printf (c"%d %f %d\n",1,2.2,3);
C_hack::ignore$ printf (c"%s %s %s\n",c"1",c"2",c"3");

The current situation with coercions is: if the parameter has ellipsis,
then a coercion casting an argument to it is simply dropped.

The unification engine will only allow a match is the leading components
of the ellipsis tailed tuple match the leading components of the argument.
The argument must has at least the required number of arguments.
However the ellipsis can match nothing (in principle a unit).

if the argument *happens* to have a tuple in the ellipsis position
it will be unpacked. In theory this is wrong. For example

1, (2,3) matches int * … and calls with 1,2,3 argument

However

1,(2,3),4 matches int * … and calls with 1,(2,3),4 argument

This is inconsistent. It’s a problem for all Felix that a tuple of one
value which happens to be a tuple itself cannot be distinguished
from the embedded tuple. Put another way an unpack on a
type variable must fail because it isn’t a tuple, but it also is
a tuple of one component, so an unpack on an instantiation
of the variable with a tuple will succeed. The behaviours are
not consistent. They should be “referentially transparent” over
instantiation.

However this stuff doesn’t matter calling C, because Felix ALREADY
hacks tuples into argument lists for C functions, and because C doesn’t
have a typle type, so passing one would always fail. [Note Felix synthesises
tuple types to types with names C can’t possibly know, so the C compiler
would always give a type error. This would apply to monomorphised C++
as well]

A Felix function with ellipsis arguments could work. Indeed the generated
class apply method just gets a … parameter too.



John Skaller
ska...@internode.on.net





John Skaller2

unread,
Feb 1, 2020, 10:41:18 PM2/1/20
to felix google
Argh. What I meant to say in the last email is that coercions of leading
components of an argument matching an ellipsis tailed tuple are
not done. They can be and should be. However it won’t cause
a problem if the coercion is one C would do anyhow. For example

long * … matches 42, “hello”

and the argument will be 42, “hello”. Unfication detects that int
is a subtype of long, so the match succeeds. The coercion is thrown
out. Felix should coerce 42 to long but doesn’t.

This works though because C does the coercion.




John Skaller
ska...@internode.on.net





John Skaller2

unread,
Feb 7, 2020, 12:42:26 AM2/7/20
to felix google
I have just added the ability to run swiftc, the Swift command line compiler,
to the flx tool:

flx —swift x.swift

It compiles the switf ok but can’t build an exe or whatever.

Flx uses toolchains for C and C++ compiles. These are OS and compiler
specific shared libraries (frozen into flx but in principle plugins) which
support a fixed set of operations:

compile for static
compie for dynamic
link exe
link shared lib
link static archive (of static objects)
link static archive (of dynamic objects)

And of course we can translate Felix to C++.
In addition, there is a dependency checker for each operation.


Felix uses Ocaml to build the compiler, there is a special tool for building
Ocaml libraries and exes. The Felix run time is also built by a special too,
it doesn’t use “flx” as such.

Swift actually makes objects compatible with C and C++ on Mac.
I can link with clang, and the link works. The run time loading fails because
I can’t figure out how to find the libraries (dlibs).

Flx also builds some Ocamls.

Now the point has come where the top level of flx is a hack. It does argument parsing,
and then tries to figure out what to do. I does dependency checking to expand the list
of files to process. But it really only does Ocaml and swift with hackery based on global
variables.

I readlly need to make flx more modular.


John Skaller
ska...@internode.on.net





Reply all
Reply to author
Forward
0 new messages