On 16/09/2014, at 1:57 PM, srean wrote:

> Now I am totally lost, sorry, could you do a redux (to a 5 yr old).

Just see the next post. It's already implemented, I just didn't realise :)

>  I have a vague suspicion that this has been provoked by Ryan's code,

Actually, I was looking at a rope made using RAList (purely functional
random access list, already coded in the library).

The problem is you can't pattern match on it like an ordinary list.

I'm so used to using pattern matching now I can't abide using a language
without it. For example Scheme is used to make the parser, it has no pattern
matching and it sucks doing anything in it which involves translating
S-expressions: I would need an arcane list of comparators applied
like (if (cond) (if (cond) .... to match a term and that is beyond my small 
brain.

So I want to bed worrying about how to make a rope and dreamed
up the general requirements for pattern matching. I think it's
a categorical adjunction but I'm not sure: you basically have to have
a partial inverse function, eg:

        Uncons (Cons (a,b)) = (a,b)

Uncons isn't an inverse for lists: it only "inverts" a list made by Cons.
So if your data type has N constructors you need N matching
destructors to take them apart and get the constructor argument
back.

So fine, but you need to know which constructor was applied,
for a standard variant type its in the value as a integer tag,
and so a match just does a test on the tag to see which
destructor to apply, and how to cast the resulting argument
to the right type.

So basically for a constructor

        Ctor: A -> U

you need two functions for union type U:

        _match_ctor_Ctor: U -> bool   // match checker
        _ctor_arg_Ctor : U -> A         // extractor

where the understanding is it is only safe to apply the extractor if the
checker returns true. SO basically a match:

        match u with
        | Ctor ?a => ...

is implemented by:

        if _match_ctor_Ctor (u) then 
                var a :A = _ctor_arg Ctor (u)'
                ....
        elif ...

This is actually implemented already! 

For an actual Felix union type
the only difference is the compiler doesn't actually put the
_match_ctor and _ctor_arg functions into the symbol table, it just
generates their definition bodies "on the fly". In fact it USED to put
them into the symbol table (which made the symbol table very big).

Since  union is generally:

        struct _uctor_ { int variant; void *data; }

the match checker just compares variant with an integer
constant corresponding to the index of the constructor
in the union definition. Eg in a list, Empty=0, Cons=1.
And the argument extractor is just a cast and deref:

        *(A*)u.data

where T is the argument type. The argument of Cons is a tuple

        T * list[T]

> then its good, because i would rather have that tied up neatly before moving 
> to something else. That Python is 3X faster is an unpardonable affront :) 

Not really. Python has very good string handling, it evolved over a long time,
and being an actual scripting language, if the string operations are fast
enough the cost of interpreting bytecode is small compared to the string ops,
even with dynamic typing. 

Felix can do it faster than Python, but the simple idioms, stuff like
iterators,  for example, is coded with the constraint of using C++
underneath, string class in particular, and wanting to be easy
to code. The easy to code stuff isn't necessarily fast and its hard
to optimise because the compiler doesn't know what a string is.

Python doesn't either! So its no excuse. However Python
is object based and strings are immutable so passing them
around has a low cost. C++ strings aren't so the cost is quite high.
Gcc uses COW but that is banned now and not allowed by the
Standard, although now we have move constructors which should
copy rvalue strings at zero cost (i.e. constant time).

So without some work, things like split are going to be expensive
because they return 2 strings from one, so you not only get two new
strings, if you're not careful they get copied in the process of returning
them. And a string will get copied when passing it as an argument too.
This is a really good reason NOT to use eager evaluation!!
It's likely to say:

        parameter = argument

which will copy argument to parameter (even if the argument is
already a single variable). Of course in Felix the parameter is then
stored into the functions class as a variable .. that's ANOTHER copy.

You can see why lazy evaluation and inlining are vital for performance:
it elides a lot of copying.

Still, Felix "style" more or less dictates pass by value and don't worry about 
it.
When the underlying types are expensive to copy this is going to rub off
if they can't do it smartly: C++ rvalue binders and thus move constructors
should help to fix this to some extent. And they had better, since now
COW strings aren't allowed.

--
john skaller
skal...@users.sourceforge.net
http://felix-lang.org




------------------------------------------------------------------------------
Want excitement?
Manually upgrade your production database.
When you want reliability, choose Perforce.
Perforce version control. Predictably reliable.
http://pubads.g.doubleclick.net/gampad/clk?id=157508191&iu=/4140/ostg.clktrk
_______________________________________________
Felix-language mailing list
Felix-language@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/felix-language

Reply via email to