On Thu, 2006-08-31 at 14:01 -0700, Erick Tryzelaar wrote:
> Martin Percossi wrote:

> Second, felix implements overloading by only overloading on 
> the first argument to a curried function. 

This isn't true anymore .. It overloads on all arguments now!
It overloads on the first argument, and then refines
the resulting set using the second, third, etc :)

What's more it ALSO uses constraints on type variables
during overloading. It cannot detect when one constraint
is 'stronger' than another, but if an overload candidate
fails to satisfy a constraint, the candidate is rejected.

> This is why our add functions 
> take a tuple as the first argument, instead of the curried "int -> int 
> -> int" form in most functional languages.

That's the original reason. There's another more insidious one:
I have more faith in the efficiency of tupled arguments,
since making curried arguments efficient depends on the
inliner being able to eliminate closures.

It CAN do that in some cases, but historically it wasn't
very good at it, and I'm still not sure how good it is.
Unfortunately if it fails, this has an effect on the
generated code and performance .. but not on the semantics,
so it is very hard to measure.

To do this I need the 'speed' system to be able to
run multiple felices and graph all the results, so I can
check visually there is no performance regression when
I 'upgrade' the optimiser to handle cases it currently
doesn't.

I do have some hack scripts for this: the 'fcount' stuff
you may have noticed actually greps the C++ and looks
for magical comments, so as to count the number of generated
functions. The stats are then added into a database (actually
a Python dictionary) for comparison.

It's a bit lame -- but visual inspection of the C++ codes
every run for every test case isn't on.

Felix, unlike Haskell, uses localised pattern matching
for closure elimination. It catches a lot of cases Haskells
systematic analysis doesn't .. but because it is just a 
localised heuristic isn't to be trusted.

To eliminate more closures requires data flow analysis..
and that's hard!

I'll wait until users have 10,000 line Felix programs
and complain about the size of generated code -- otherwise
it is just too hard to test the results.

>  Since felix can't currently 
> take the value of an overloaded function (for example: "val f = add"), 
> you need to select which overload you want to use (the "of (int*int)").

Variables can't contain sets of functions. But that's a minor thing.
The MAJOR problem is one you may miss if you weren't used to
a real FPL: variables can't contain polymorphic functions either.
Closures are monomorphic.

In Ocaml, functions are polymorphic because of boxing: it uses
intensional polymorphism. Felix uses extensional polymorphism
like C++. The only way to get intensional polymorphism is
with primitives (using void* and casts).

This will be fixed at some stage in various ways:
boxed values, and RTTI extension so even expanded types
can be copied about at run time.

> On a side note, John, how impossible would it be to have these 
> first-class overloaded functions?

It is possible. The encoding is tricky but basically:

        fun f(x:int): int => ...
        fun g(x:long): int => ...

can be unified in the usual way:

        fun fg(x: int + long):int=
        | case 0 (?i) => f i
        | case 1 (?l) => g l
        ;

right now. So clearly, the compiler could generate such
a unified function for a given overload set. Note the
return types have to be the same.

however, you can't use it like:

        fg 1;

because 1 is not of type int + long. Instead you have to
use it like

        fg (case 0 of (int + long) 1)

to convert the integer into a tagged variant. Easier with
named unions:

        fg$ Int 1
        fg$ Long 1L

So it isn't clear this buys you very much. Better can be done,
with some kind of implicit union of all types, that is,
using RTTI as the discriminant, but to avoid a run time
error you'd have to define the function for ALL types than.

I propose to do that for some functions such as:

        copy
        init
        remap (in place after a mmap)
        assign
        destroy (already generated by the compiler)
        move

and later

        eq
        compare
        serialise_out
        serialise_in


and some other important generics. This turns the
shape object into a virtual table, which is exactly what
Python has right now .. I did mention Felix was a scripting
language?

Most such languages start with dynamic typing and collapse
when they try to optimise with static typing.

Felix starts with strong static typing .. the dynamic typing
gets added later so it will already be able to do heavy
optimisation and yield high performance: Python on steroids :)
        

> > 2)
> > myAdd3 x = x + 3
> > myAdd3PointFree = (+ 3)
> >   
> 
> That you'd have to curry them yourself:
> 
> fun addi (x:int) (y:int) => x + y;
> 
> val myAdd3PointFree = (addi 3);

There is a very important categorical mapping between


        f: int * int -> int
        f: int -> int -> int

This 'more or less' should be generated automatically.
Exactly how 'more' or 'less' I don't know, since it
will interfere with overloading.


-- 
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net


-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Felix-language mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/felix-language

Reply via email to