Saturday, January 30, 2010

Throw & Catch

I didn't implement exceptions right, yet. Throws and catches are factored out but catches catch the exception local in the term. I looked at call-cc and reset-shift, couldn't decide, and, once again, I am going for my own most straightforward implementation described below.

A ((try E0 catch E1) exc) block is roughly translated to (R (λs. E0 (λx.s (E1 x))))  which evaluates to ((λs. E0 (λx. s (E1 x))) Sk) , where Sk is a control object holding the continuation of the original block. It looks the most like reset-shift I guess. The difference is that, as far as I know, reset-shift is a syntactical addition to lambda calculus, whereas these combinators model roughly the same, but depend on call-by-value evaluation to express a control operator which you can't model directly syntactically using common combinators.

R takes a function f and rewrites to f SkSk takes a value v and rewrites to v, but escapes the local context up to the point R was applied.

I didn't implement it yet. Worried a bit about the shift combinator escaping the local context.

013110: Convinced myself, and implemented it. Since the try part of a try-catch can only evaluate to a value or an exception, the exception handler is either applied, or not, so should be properly discarded.

Food is a nice idea sometimes too.

Monday, January 25, 2010

Combinators, Calls and FFI

I threw away too much code to arrive at something in which I was unable to express certain basic functionality. The good thing was that it cleaned up some badly thought trough lines. I ended up with some small changes in the runtime, and again, I find myself writing some lines to throw away other lines. Write-off: learning.

012610: I added marshalling code, it uses a recursive C routine and probably needs a terabyte memory, but... its there. I could always zip of course. Unmarshalling not there yet. Added a symbol table with linear lookup. Last Optimize?

The only women I love: Amy Winehouse.

Sunday, January 24, 2010

Symbol Tables...

Right, you think you can do without, but you can't. If I (un-)marshal code, I need a manner of binding thunks to C functions. So, a symbol table, or I compile a program to a DLL, and abuse the fact that I can reflect on that.

Serious introspection.

Tuesday, January 19, 2010

Position Based Parsing

I took a one-week trip into QM land, which ended, took a fresh look at my position based parser combinators, and saw that I am going at it the wrong way. A beginners error, if you thread positions along, which you need in position based parsing, it also needs to be returned.

In need of steam.

Tuesday, January 12, 2010

Open Source...

I gave it some thought. Preferably, I'll go the following route: close source with a few people, and then open source, or half license, it. Then see where it'll stick.

Monday, January 11, 2010

Earliest Deadline Gone to ReST

Its can-read-but-cannot-write time again, unfortunately. The bootstrap compiler should run, but misses marshalling primitives in C. An object file is nothing more than a marshalled simplified AST which passed all semantic checks, so those primitives should be added to arrive at a full bootstrap compiler.

I read a new ParaSail post on tasks, which basically gives an overview of scheduling terminology. I am looking at mutlithreading too. In my case, designing given the current runtime boils down to choosing between alternatives which I can expect give reasonable performance. So I am looking at how to handle memory, what to do explicit and implicit, and how to combine FFI and the environment - possibly combine low-level task primitives with marshalling code.

There is a simple scheme I think will work out nicely, but has two drawbacks, 1) explicit management of memory, and 2) some need for synchronization beyond what I like, or rather, how to implement a join, or signals.  I'll do something simplistic on which I expect different solutions can be build, EDF scheduling is a bit too much at this point, but should be implementable given what I am aiming for.

Needed some rest, so writing on the ReST parser - see if I can get the bootstrap to output documentation before a complete self compile.


011210: Cleaning up again, writing some position based parser combinators. Ugly and slow but sufficient. I don't extract blocks from the source, but the penalty is a large number of redundant positional checks. I should get my hands on: S. Doaitse Swierstra and Pablo R. Azero Alcocer, Fast, Error Correcting Parser Combinators: A Short Tutorial. In Lecture Notes in Computer Science, SOFSEM’99: Theory and Practice of Informatics, volume 1725/2009, page 763. Friday, January 01, 1999. Nice-to-have for the Hi grammar parser I guess.

Saturday, January 9, 2010

Back

I had a slow week I guess, looking at code. Bug must be somewhere between eta-expansion and combinator lifting. Must be in the partial evaluator, some sloppy substitution...

011110: What a date! Removed the bug in alpha-conversion, a forgotten one-liner actually. At a pace of one bug a week, I better not have too many. The program print (10, false, "hello world!") now works, which means Currying, method calls, FFI largely works.

All programs have at least one  superfluous instruction and at least one bug. Therefore, it follows by induction that every program can be reduced to one flawed instruction.

Wednesday, January 6, 2010

Propagation Networks

I read the PhD Thesis of Radul, or, sort of skimmed it. It reminded me a lot of a combination of stream processing functions, popularized by Broy among a lot of others, and visual languages such as LabView and Simulink. It pointed me in the way of how to deal with multicore, at least, on a high level for Hi. It won't be as fast as C, it can never be in the end, but yeah, it'll be cool.

Alexey Andreyevich Radul, Propagation Networks: A Flexible and Expressive Substrate for Computation, Massachusetts Institute of Technology, September 2009.

Tracking

Tracking possible security breaches is an energy drag, started coding again. It doesn't seem to be the guards, it must be some translation bug on complex terms. Doing TDD must reveal it at some point, but I am not entirely happy with that direction. I.e. it should be easier to determine whether bugs are in: the original AST, the intermediate LC, or the abstract assembly.

I could build 'partial' evaluators for all three, and compare results between transformations -even intermediates on intermediates,- but that's a drag too. Next step is?

010710: Next step is better tests. You'll need them anyway, and it turned out to be the guards.

Monday, January 4, 2010

ParaSail

Added an entry to the language 'ParaSail,' which does a lot the same, and also a lot different. The type system is in essence the same, it defines regions, it uses an older syntax closer to the Algol, Modula, Pascal, Ada tradition. Whereas Hi is to Haskell as what Javascript is to C, it seems to pitch itself as a beefed up parallel Ada.

When it comes to multicore, it makes a number of decisions, I would do a lot different. It seems to aim at fine grained parallelism. Essentially, the road I am planning to take is: since all parallelism implies overhead, don't do it, and go for coarse grained parallelism.

It has a different string type, which Ocaml has for performance, and Lisp had for the same reason, and subsequently abandoned. (Lisp is fast on lists/list processing functions, and sharing is easier on series of chars than parts of strings - there was no performance gain.)

Yeah, MacOS or FreeBSD, definitely.

Sunday, January 3, 2010

System is Compromised?

It seems the Mozilla package, or Javascript, is compromised. Various Http connections are opened to machines, someone is tunneling to this system. Which is very bad on a Unix system since everyone can do anything like its me.

Now what does that mean for a language? Especially, if you were hoping you could make money on it? Ah well, finish it anyway. Its not like it was working at the moment.

Ah well, guess it shows me for not changing the root password over the last ten years.

010710: The Mozilla hack is pretty much confirmed. Guess European and US ITs can be pretty sure they're giving away their code/hardware designs.

Move to MacOs?

Log 010310

Long compiles. Misinterpretation bugs with C - int_compare, int_less, bool conversion. Evaluation seems in order now, unit tests needed on bindings to DLs. Failure to print the constant 10 hinting at a logical error in translating guards.

Hey, it took a lot of people a long time to get 0 right too!

Breakdown

A highly unsophisticated breakdown of grep/wc. Looks pretty functional to me: Lots of stuff moving around.
counting words in test_print_tuple.s
add         :  6855
and         :   372
call        :   472
cltq        :   299
cmov        :   302
cmp         :  6595
ja          :  2390
jb          :  1812
je          :  4936
jg          :   304
jle         :   305
jmp         :  1431
jne         :   718
lea         :  8010
mov         : 27955
pop         :  4181
push        :  1797
pxor        :   297
ret         :   870
salq        :   609
shr         :  1190
sub         :   729
test        :  2203
xorl        :  1943
\.string    :   574

Given everything, a factor 3-4 bigger in size than Ocaml isn't even that bad, for a first version which does no optimization and compiles to C. I don't have a stack, so every basic assignment translates to roughly two moves instead of a move and a relatively light push. I don't factor out string constants, so there are spurious calls to strneq. Each pattern match does an extra structure test on a record, for safety, at the moment.

Its hard not to optimize. Factoring out strings is easy, moving to bytecode too. But factoring out strings will make it harder to generate thunks 'on-the-fly,' which I want for a REPL, and moving to bytecode means an own optimizer, whereas I now leave that to C, which compiles to way more code than I expected. The usual route for bytecode is that performance drives implementers to optimizations such as hotspot compiles, which means dynamically ending up with the same, if not more, machine code in memory. Bytecode interpretation often remains faster than native code due to less cache misses, so its still a good option.

Still, some small changes and I might end up with 1.5x with respect to Ocaml? And 27k moves for 1kLoC source code? It might pay of to write my own optimizer on the generated intermediate assembly since the compiler knows best it is basically just shifting constants around.

I need a comparison between what assembly is, and should be, generated.

Note: Its all a first glance.

I have no humour

Saturday, January 2, 2010

CLang

I generate C, but its more glorified assembly than anything else. I need an optimizer which optimizes assembly to the bone. I missed the LLVM adoption by Apple news, switching compilers might be a good trick here. Not sure how much libffi is tied to GCC though.

010310: I would need gcc-llvm or Clang. LLVM doesn't seem to beat gcc often yet.

FFI ready, but no C++

System Boundaries and Interfaces

If possible, make sure your code agrees on the interpretation of what variables mean what according to whom. Rethinking, looks like a renaming bug?
list.cons v2 v0 = v2;

Found it. Safe in LC, but cannot be translated directly.

Too bored to fix bugs?

Log 010210

Hello World. Fixed a bug in '@.' Some documentation work. Ended the day with a core dump...

Generated programs are huge. Not sure it's because of compiled out constants or large routines, looking for an analyser for binary/C programs. Looks like it is the cost of not having bytecode, possibly the cost of not compiling to machine code directly. Plus there's some overhead for using the C calling convention and some overhead for generated constants. Are there static analysers which dump data on calling overhead, constants, and assembly?

With -Os, optimize for size, I'll be looking at x3-4 size w.r.t. Ocaml, and -O9, optimize for speed, x10-15 w.r.t Ocaml. Which is interesting, since I don't see how -O9 could be faster than -Os, on this generated code?

All generated variables are in SSA form, all data can be treated as constants. How to tell C?

[marco@horse tests]$ make system/test_print_tuple.out
gcc -g -I /usr/lib64/libffi-3.0.5/include -lffi -l dl -I ../runtime/ system/test_print_tuple.c -o system/test_print_tuple.out
[marco@horse tests]$ ./system/test_print_tuple.out

evaluating : (_MAIN )

evaluating : (___exception )

evaluating : (main (___exception ))

evaluating : (system.print (___exception )(system.tuple_3 10 (system.false )(system._txt (list.cons h (list.cons e (list.cons l (list.cons l (list.cons o (list.cons   (list.cons w (list.cons o (list.cons r (list.cons l (list.cons d (list.nil )))))))))))))))

evaluating : (_select_system.to_text (___exception )(system.tuple_3 10 (system.false )(system._txt (list.cons h (list.cons e (list.cons l (list.cons l (list.cons o (list.cons   (list.cons w (list.cons o (list.cons r (list.cons l (list.cons d (list.nil ))))))))))))))(system.tuple_3 10 (system.false )(system._txt (list.cons h (list.cons e (list.cons l (list.cons l (list.cons o (list.cons   (list.cons w (list.cons o (list.cons r (list.cons l (list.cons d (list.nil )))))))))))))))

evaluating : (_select_system.to_text130484 (___exception )(system.tuple_3 10 (system.false )(system._txt (list.cons h (list.cons e (list.cons l (list.cons l (list.cons o (list.cons   (list.cons w (list.cons o (list.cons r (list.cons l (list.cons d (list.nil )))))))))))))))

evaluating : (system.txt (___exception )(system._txt (list.cons h (list.cons e (list.cons l (list.cons l (list.cons o (list.cons   (list.cons w (list.cons o (list.cons r (list.cons l (list.cons d (list.nil ))))))))))))))

evaluating : (_select_system.to_text (___exception )(system._txt (list.cons h (list.cons e (list.cons l (list.cons l (list.cons o (list.cons   (list.cons w (list.cons o (list.cons r (list.cons l (list.cons d (list.nil )))))))))))))(system._txt (list.cons h (list.cons e (list.cons l (list.cons l (list.cons o (list.cons   (list.cons w (list.cons o (list.cons r (list.cons l (list.cons d (list.nil ))))))))))))))

evaluating : (_select_system.to_text130494 (system._txt (list.cons h (list.cons e (list.cons l (list.cons l (list.cons o (list.cons   (list.cons w (list.cons o (list.cons r (list.cons l (list.cons d (list.nil ))))))))))))))

evaluating : (system.concat (___exception )(system._txt (list.cons h (list.cons e (list.cons l (list.cons l (list.cons o (list.cons   (list.cons w (list.cons o (list.cons r (list.cons l (list.cons d (list.nil )))))))))))))(system._txt (list.cons ) (list.nil ))))

evaluating : (system.concat130501 (___exception ))

evaluating : (system.fix (___exception )(system.concat130501 (___exception )))

evaluating : (system.fix130570 (___exception )(system.concat130501 (___exception )))

evaluating : (___apply (system.concat130501 (___exception ))(system.fix130570 (___exception )(system.concat130501 (___exception ))))

evaluating : (system.concat130501 (___exception )(system.fix130570 (___exception )(system.concat130501 (___exception ))))

evaluating : (system.concat130499 (___exception )(system.concat130501 (___exception )(system.fix130570 (___exception )(system.concat130501 (___exception ))))(system._txt (list.cons h (list.cons e (list.cons l (list.cons l (list.cons o (list.cons   (list.cons w (list.cons o (list.cons r (list.cons l (list.cons d (list.nil )))))))))))))(system._txt (list.cons ) (list.nil ))))

evaluating : (___apply (system.concat130501 (___exception )(system.fix130570 (___exception )(system.concat130501 (___exception ))))(list.cons h (list.cons e (list.cons l (list.cons l (list.cons o (list.cons   (list.cons w (list.cons o (list.cons r (list.cons l (list.cons d (list.nil ))))))))))))(list.cons ) (list.nil )))

evaluating : (system.concat130501 (___exception )(system.fix130570 (___exception )(system.concat130501 (___exception )))(list.cons h (list.cons e (list.cons l (list.cons l (list.cons o (list.cons   (list.cons w (list.cons o (list.cons r (list.cons l (list.cons d (list.nil ))))))))))))(list.cons ) (list.nil )))

evaluating : (___apply (system.fix130570 (___exception )(system.concat130501 (___exception )))(null)
(list.cons ) (list.nil )))


...
 

Segmentation fault (core dumped)
[marco@horse tests]$ echo "uh, null?"


When you're talking to your computer, you're a hacker. When your computer talks back to you, you're schizo.

Friday, January 1, 2010

Constants To Functions

The better title would be "How to simulate Eagerness Lazily." But then, there's no solution yet.

The question is how in a lazy LCi calculus it is enforced that a call is strict in it's arguments. The answer, just doodling here, might be to shift the meaning of constants such that they are applied to the calls, i.e. constants are functions, under lazy reduction. A bit dirty, since constants and calls are seen as separate entities -have different semantics- under different rewrite strategies.

Thus, 'print (1 + 2) * 3' would be compiled to '((((2 1) +) 3) *) print,' where a bold emphasized constant denotes a lazy constant. It would reduce the following way:

    ((((2 1) +) 3) *) print
→  {reduce 2 1 to F, F expects a function taking two integers} 
    (((F +) 3) *) print
→  {reduce F + to 3}
     ((3 3) *) print
→  {reduce 3 3 to G}
     (G *) print
→  {reduce G * to 9}
     9 print


Ah well, reverse polish on a stack machine. Guess if you would know LC/ Haskell better than me this would be a standard technique. Or, should it be: '((((2 +) 1) *) 3) print'?

It breaks, for now, but the answer is somewhere there, I guess. Is '((1 2) +) (( 1 2) +) *' valid? Or, can you assume it would just never be generated? When an argument is fully reduced, swap the next argument to head position? Can you guarantee that a swap is the last thing to do lazily? It might just be impossible? I need a book...

The central idea: make arguments functions, evaluate arguments first, swap each argument with the next argument when it is reduced.


010210: I am pretty sure I am reinventing the encoding of integers in LC here, or something close to it. Just use those, or a more concrete representation, and make a call continue by evaluating the next call could be sufficient.


Back to C


Hello 2010!

Small steps, next result, which always is good for a smile. The next 'hello world' program compiled to C and ran.


import "system.hi"

using system

def main: unit =
    _ = print "Hello 2010!";
    nop 


Which for most scripting languages would be totally uninteresting, but in this case is somewhat more interesting, since I linked it with this:


namespace system (

    using string

    interface Text = \=> #where (
        def to_text: t -> text
    )

    def print: ::Text t => t -> unit =
        [ t -> print_text (t.to_text t) ]

    def txt: ::Text t => t -> text =
        [ t -> t.to_text t ]

    def print_text: text -> unit =
        [ t ->
            f = get_stdout nop;
            u = file_print f (str t);
              u ]
)

namespace system (

    interface Length = \=> #where (
        def to_length: a -> int
    )

    def length: ::Length t => t -> int =
        \-> x.to_length x
)

namespace list (

    using system

    type list = \=> [nil | cons a (list a)]

    def list_compare: ::Ord a => list a -> list a -> int =
        [ nil      , nil       ->   0 
        | nil      , _         -> 0-1
        | _        , nil       ->   1
        | cons x xx, cons y yy -> 
            if x == y then xx.compare xx yy
            else x.compare x y ]

    def list_length: list a -> int =
        [ nil       -> 0
        | cons x xx -> 1 + (list_length xx) ]

    instance ::Ord a => Ord (list a) where (
        def compare: list a -> list a -> int = list_compare
    )

    def list_to_text: ::Text t => list t -> text = 
        u = [_txt cc -> cc];
        c = [ x -> u (x.to_text x) ];
        m = fix [ m, f, nil -> nil
                | m, f, cons x xx -> cons (f x) (m f xx) ];
        p = fix [ p, nil, yy       -> yy
                | p, cons x xx, yy -> cons x (p xx yy) ];
        f = fix [ f, nil -> nil
                | f, cons x xx -> p x (f xx) ];
        x = cons '[' nil; y = cons ',' nil; z = cons ']' nil;
        // x = u "["; // y = u ", "; z = u "]";
        t = fix [ t, nil -> nil
                | t, cons x nil -> cons x nil
                | t, cons x0 (cons x1 xx) 
                    -> cons x0 (cons y ((cons x1 xx))) ];
        [ xx -> 
            yy = m c xx;
            yy = t yy;
            yy = f yy;
            yy = f (cons x (cons yy (cons z nil)));
                _txt yy ]

    instance ::Text t => Text (list t) where (
        def to_text: list t -> text = list_to_text
    )

    instance Length (list t) where (
        def to_length: list t -> int = list_length
    )
)

namespace string (

    using system
    using list

    type string = list char

    def text_to_string: text -> string =
        [ _txt cc -> cc ]

    def string_to_text: string -> text =
        [ cc -> _txt cc ]

    def lift: (string -> string) -> text -> text =
        [ f, _txt cc -> _txt (f cc) ]

    interface Str = \=> #where (
        def to_string: t -> string
    )

    def str: ::Str t => t -> string =
        [ t -> t.to_string t ]

    instance Str bool where (
        def to_string: bool -> string = 
            [ b -> text_to_string (txt b) ]
    )

    instance Str char where (
        def to_string: char -> string = 
            [ c -> text_to_string (txt c) ]
    )

    instance Str int where (
        def to_string: int -> string = 
            [ i -> text_to_string (txt i) ]
    )

    instance Str text where (
        def to_string: text -> string = 
            [ t -> text_to_string t ]
    )

)

namespace system (

    using string

    def trace: ::Text t => t -> t =
        [ t ->
            _ = print "(trace:";
            _ = print t;
            _ = print ")\n";
            t ]

    def trace_application: ::Text a ::Text b => (-> b) -> a -> b =
        [ f, x -> [ x, y ->
            _ = print "(trace:";
            _ = print x;
            _ = print " -> ";
            _ = print y;
            _ = print ")\n";
                y ] x (f x) ]

    def banner: ::Text a => a -> unit =
        stars = fix 
            [ st, 0 -> list.nil
            | st, n -> list.cons '*' (st (- 1)) ];
        p = fix 
            [ p, list.nil,       xx -> xx
            | p, list.cons y yy, xx -> list.cons y (p yy xx) ];
        [ t ->
            t = str (txt t);
            l = length (text_to_string (txt t));
            l0 = 70 - l; l1 = l0 / 2; l2 = l0 - l1;
            t = p (stars l1) (p t (stars l2));
            print (string_to_text t) ]
)

namespace system (

    interface Num = \=> #where (
        def plus: a -> a -> a
        def min: a -> a -> a
        def mul: a -> a -> a
        def div: a -> a -> a
    )

    def +: ::Num a => a -> a -> a = 
        \x,y -> x.plus x y

    def -: ::Num a => a -> a -> a = 
        \x,y -> x.min x y
    
    def *: ::Num a => a -> a -> a = 
        \x,y -> x.mul x y

    def /: ::Num a => a -> a -> a = 
        \x,y -> x.div x y

)


namespace system (

    interface Ord = \=> #where (
        def compare: a -> a -> int
    )

    def ==: ::Ord a => a -> a -> bool =
        \x,y -> int_eq (x.compare x y) 0

    def !=: ::Ord a => a -> a -> bool =
        \x,y -> int_eq (x.compare x y) 1

    def <: ::Ord a => a -> a -> bool =
        \x,y -> int_less (x.compare x y) 0

    def <=: ::Ord a => a -> a -> bool =
        \x,y -> int_less (x.compare x y) 1

    def >: ::Ord a => a -> a -> bool =
        \x,y -> int_less (x.compare y x) 0

    def >=: ::Ord a => a -> a -> bool =
        \x,y -> int_less (x.compare y x) 1

    def comp1: ::Ord a => a -> a -> int =
        [ x0,x1 -> x0.compare x0 x1 ]

    def comp2: ::Ord a => ::Ord b => 
        a -> a -> b -> b -> int =
        [ x0,x1,y0,y1 -> 
            c = x0.compare x0 x1;
            if c == 0 then y0.compare y0 y1 else c ]

    def comp3: ::Ord a ::Ord b ::Ord c => 
        a -> a -> b -> b -> c -> c -> int =
        [ x0,x1,y0,y1,z0,z1 -> 
            c =  x0.compare x0 x1;
            if c == 0 then comp2 y0 y1 z0 z1 else c ]

    def comp4: ::Ord a ::Ord b ::Ord c ::Ord d =>
        a -> a -> b -> b -> c -> c -> d -> d -> int =
        [ x0,x1,y0,y1,z0,z1,p0,p1 -> 
            c = comp2 x0 x1 y0 y1;
            if c == 0 then comp2 z0 z1 p0 p1 else c ]

    def comp5: ::Ord a ::Ord b ::Ord c ::Ord d ::Ord e =>
        a -> a -> b -> b -> c -> c -> d -> d -> e -> e -> int =
        [ x0,x1,y0,y1,z0,z1,p0,p1,q0,q1 -> 
            c = comp2 x0 x1 y0 y1;
            if c == 0 then comp3 z0 z1 p0 p1 q0 q1 else c ]

    def comp6: ::Ord a ::Ord b ::Ord c ::Ord d ::Ord e ::Ord f =>
        a -> a -> b -> b -> c -> c -> d -> d -> e -> e -> f -> f -> int =
        [ x0,x1,y0,y1,z0,z1,p0,p1,q0,q1,r0,r1 -> 
            c = comp3 x0 x1 y0 y1 z0 z1;
            if c == 0 then comp3 p0 p1 q0 q1 r0 r1 else c ]
)

namespace system (

    def fix: ((-> b) -> a -> b) -> a -> b =
        [ f -> f [-> (fix f) x] ]

    def id: t -> t = [ x -> x ]

)

namespace system (

    type unit = [ nop ]

    def voiden: a -> unit = [ _ -> nop ]

)

namespace system (

    using system

    using Num

    type bool = [ true | false ]

    def not: bool -> bool =
    [ true -> false 
    | _    -> true ]

    def and: bool -> bool -> bool =
    [ truetrue  -> true 
    | _   , __    -> false ]

    def or: bool -> bool -> bool =
    [ falsefalse -> false 
    | _   , __     -> true ]

    def eq: bool -> bool -> bool =
    [ falsefalse -> true 
    | true , true  -> true 
    | _   , __     -> false ]

    instance Ord bool where (
        def compare: bool -> bool -> int =
        [ falsefalse -> 0
        | truetrue   -> 0
        | false, _     -> 0-1 ]
    )

    instance Text bool where (
        def to_text: bool -> text = 
        [ false -> "false"
        | _     -> "true" ]
    )

)

namespace system (

    using list

    // type int = [ MININT | ... | -1 | 0 | 1 | 2 | ... | MAXINT ]
    type int = {system.int}

    def int_monadic_min: int -> int = 
        \v0 -> {math.int_mon:int[v0:int]}

    def int_dyadic_min: int -> int -> int = 
        \v0,v1 -> {math.int_sub:int[v0:int,v1:int]}

    def int_add: int -> int -> int = 
        \v0,v1 -> {math.int_add:int[v0:int,v1:int]}

    def int_mul: int -> int -> int =
        \v0,v1 -> {math.int_mul:int[v0:int,v1:int]}

    def int_div: int -> int -> int = 
        \v0,v1 -> {math.int_div:int[v0:int,v1:int]}

    def int_compare: int -> int -> int = 
        \v0,v1 -> {math.int_compare:int[v0:int,v1:int]}

    def int_eq: int -> int -> bool = 
        \v0,v1 -> [-> true | _ -> false] (int_compare v0 v1)

    def int_less: int -> int -> bool = 
        \v0,v1 -> {math.int_less:int[v0:int,v1:bool]}

    def int_magic_tick: unit -> int = 
        \v0 -> [ v1 -> {math.int_magic_tick:int[v1:void]} ] 0

    instance Num int where (
        def plus: int -> int -> int = int_add
        def min:  int -> int -> int = int_dyadic_min
        def mul:  int -> int -> int = int_mul
        def div:  int -> int -> int = int_div
    )

    instance Ord int where (
        def compare: int -> int -> int = int_compare
    )

    def int_to_text: int -> text = 
        c = [ 0 -> '0' | 1 -> '1' | 2 -> '2' | 3 -> '3' | 4 -> '4'
            | 5 -> '5' | 6 -> '6' | 7 -> '7' | 8 -> '8' | 9 -> '9' ];
        p = fix [ p, nil,       x -> cons x nil
                | p, cons y yy, x -> cons y (p yy x) ];
        s = fix [ s, n ? n < 0    -> cons '-' ((0-n))
                | s, n ? n < 10   -> cons (c n) nil
                | s, n            ->
                    n0 = n / 10; n1 = n - (n0 * 10);
                    p (s n0) (c n1) ];
        [ n -> _txt (s n) ]

    def text_to_int: text -> int =
        dg = [ '0' -> 0 | '1' -> 1 | '2' -> 2 | '3' -> 3
             | '4' -> 4 | '5' -> 5 | '6' -> 6 | '7' -> 7
             | '8' -> 8 | '9' -> 9 | _ -> (0-1) ];
        ti = fix
            [ ti, acc, nil           -> acc
            | ti, acc, cons '-' dd   -> 0 - (ti acc dd)
            | ti, acc, cons d dd     ->
                d = dg d;
                if d < 0 then ti acc dd 
                else ti ((10 * acc) + d) dd ];
        [ _txt cc -> ti 0 cc ]

    instance Text int where (
        def to_text: int -> text = int_to_text
    )

)

namespace system (

    type char = {system.char}

    def char_ascii_code: char -> int =
        \c0 -> {math.char_ascii_code:int[c0:char]}

    def char_ascii_char: int -> char =
        \c0 -> {math.char_ascii_char:char[c0:int]}

    def char_compare: char -> char -> int = 
        \c0,c1 -> int_compare (char_ascii_code c0) (char_ascii_code c1)

    instance Ord char where (
        def compare: char -> char -> int = char_compare
    )

    instance Text char where (
        def to_text: char -> text = 
            [ c -> _txt (list.cons c list.nil) ]
    )

)

namespace system (

    using system

    using list

    using string

    type chars = { chars }

    def chars_allocate: int -> chars =
        [ sz -> {mem.allocate:pointer[sz:int]} ]

    def chars_free: chars -> unit =
        [ p -> {mem.free:void[p:pointer]} ]

    def chars_set: chars -> int -> char -> unit =
        [ p, n, v -> {mem.set_char:char[p:pointer, n:int, v:char]} ]

    def chars_get: chars -> int -> char =
        [ p, n -> {mem.get_char:char[p:pointer, n:int]} ]

    def chars_blank: chars -> int -> unit =
        [ p, n -> v = 0 ; {mem.set_byte:void[p:pointer, n:int, v:int]} ]

    def chars_is_blank: chars -> int -> bool =
        [ p, n -> 
            [ 0 -> true | _ -> false ] 
            {mem.get_byte:int[p:pointer, n:int]} ]

    def string_to_chars: list char -> chars =
        cf = fix 
            [ cf, s, n, nil -> chars_blank s n
            | cf, s, n, cons c cc -> 
                _ = chars_set s n c; cf s (int_add 1 n) cc ];
        [ cc ->
            l = length cc;
            s = chars_allocate (int_add l 1);
            _ = cf s 0 cc;
            s ]

    def chars_to_string: chars -> list char =
        cs = fix
            [ cs, p, n ->
                if chars_is_blank p n then nil 
                else cons (chars_get p n) (cs p (int_add n 1)) ];
        [ p -> cs p 0 ]

    def text_to_chars: text -> chars =
        [ t -> string_to_chars (text_to_string t) ]

    def chars_to_text: chars -> text =
        [ t -> string_to_text (chars_to_string t) ]

)

namespace system (

    using system

    using string

    using list

    type file = { file }

    def get_stdin: unit -> file =
        [ u -> {io.get_stdin:pointer[u:void]} ]

    def get_stderr: unit -> file =
        [ u -> {io.get_stderr:pointer[u:void]} ]

    def get_stdout: unit -> file =
        [ u -> {io.get_stdout:pointer[u:void]} ]

    def fgetc: file -> int =
        [ f -> {io.fgetc:int[f:pointer]} ]

    def fputc: file -> int -> unit =
        [ f, c -> {io.fputc:void[f:pointer, c:int]} ]

    def fputs: file -> chars -> unit =
        [ f, c -> {io.fputs:void[c:pointer, f:pointer]} ]

    def fput_char: file -> char -> unit =
        [ f, c -> {io.fput_char:void[f:pointer, c:char]} ]

    def fget_char: file -> char =
        [ f -> {io.fget_char:char[f:pointer]} ]

    def feof: file -> int =
        [ f -> {io.feof:int[f:pointer]} ]

    def fis: file -> int =
        [ f -> {io.fis:int[f:pointer]} ]

    def fopen: chars -> chars -> file =
        [ f, m -> {io.fopen:pointer[f:pointer, m:pointer]} ]

    def fclose: file -> int =
        [ f -> {io.fclose:int[f:pointer]} ]

    def fend: file -> bool =
        [ f -> [ 0 -> false | _ -> true ] (feof f) ]

    def mode_read: text = "r"

    def mode_read_write: text = "r+"

    def mode_write: text = "w"

    def mode_write_read: text = "w+"

    def mode_append: text = "a"

    def mode_append_read: text = "a+"

    def file_is: file -> bool =
        [ f -> [ 0 -> false | _ -> true ] (fis f) ]

    def file_open: text -> text -> file =
        [ ff, mm ->
            n = text_to_chars ff;
            m = text_to_chars mm;
            f = fopen n m;
            _ = chars_free n;
            _ = chars_free m;
                f ]

    // XXX: should be fstat, unsafe - but portable
    def file_exists: text -> bool =
        [ fn -> 
            f = file_open fn mode_read;
            _ = fclose f;
                file_is f ]

    def file_close: file -> unit =
        [ f -> n = fclose f; nop ] // XXX:should throw an exception here

    def file_input: file -> list char =
        [ f -> 
            c = fget_char f;
            if fend f then nil else cons c (file_input f) ]

    def file_print: file -> list char -> unit =
        [ f, nil        -> nop
        | f, cons c cc  -> _ = fput_char f c; file_print f cc ]

    def file_read: text -> text =
        [ ff ->
            f  = file_open ff mode_read;
            cc = file_input f;
            u  = file_close f;
                string_to_text cc ]

    def file_write: text -> text -> unit =
        [ fn, t ->
            f  = file_open fn mode_write;
            _  = file_print f (str t);
            u  = file_close f;
                u ]

    def file_scribe: text -> list text -> unit =
        fp = fix 
            [ fp, f, nil        -> nop
            | fp, f, cons t tt  -> 
                _ = file_print f (str t); fp f tt ];
        [ fn, tt ->
            f  = file_open fn mode_write;
            _  = fp f tt;
            u  = file_close f;
                u ]

    def argc: unit -> int =
        [ u -> {io.get_argc:int[u:void]} ]

    def argv: int -> text =
        [ n -> 
            s = {io.get_argv:pointer[n:int]}; 
            t = chars_to_text s;
                t ]

    def args: unit -> list text  = 
        aa = fix
            [ aa, n ->
                if n >= (argc nop) then nil 
                else cons (argv n) (aa (n+1))];
        [-> aa 0 ]

)

namespace system (

    using list

    using string

    type text = [ _txt (list char) ]

    instance Text text where (
        def to_text: text -> text = [ t -> t ]
    )

    instance Ord text where (
        def compare: text -> text -> int = 
        [ _txt t0, _txt t1 -> list_compare t0 t1 ]
    )

    def concat: text -> text -> text =
        c = fix [ c, nil, yy       -> yy
                | c, cons x xx, yy -> cons x (c xx yy) ];
        [ _txt cc0, _txt cc1 -> _txt (c cc0 cc1) ]

    def text_to_string: text -> list char = 
        [ _txt cc -> cc ]

    def squash: list text -> text =
        [ nil       -> ""
        | cons t tt -> concat t (squash tt) ]

    instance Length text where (
        def to_length: text -> int = \-> length (str t)
    )


)

namespace system (

    type tuple_t0 =  
        [ tuple_0 ]

    type tuple_t1 = \t0 => 
        [ tuple_1 t0 ]

    type tuple_t2 = \t0 \t1 => 
        [ tuple_2 t0 t1 ]

    type tuple_t3 = \t0 \t1 \t2 => 
        [ tuple_3 t0 t1 t2 ]

    type tuple_t4 = \t0 \t1 \t2 \t3 => 
        [ tuple_4 t0 t1 t2 t3 ]

    type tuple_t5 = \t0 \t1 \t2 \t3 \t4 => 
        [ tuple_5 t0 t1 t2 t3 t4 ]

    type tuple_t6 = \t0 \t1 \t2 \t3 \t4 \t5 => 
        [ tuple_6 t0 t1 t2 t3 t4 t5 ]

    type tuple_t7 = \t0 \t1 \t2 \t3 \t4 \t5 \t6 => 
        [ tuple_7 t0 t1 t2 t3 t4 t5 t6 ]

    type tuple_t8 = \t0 \t1 \t2 \t3 \t4 \t5 \t6 \t7 => 
        [ tuple_8 t0 t1 t2 t3 t4 t5 t6 t7 ]

    type tuple_t9 = \t0 \t1 \t2 \t3 \t4 \t5 \t6 \t7 \t8 => 
        [ tuple_9 t0 t1 t2 t3 t4 t5 t6 t7 t8 ]

    instance 
        Text tuple_t0 where (
        def to_text: tuple_t0 -> text =
            [ tuple_0 -> "()" ]
    )

    instance 
        ::Text t0 =>
        Text (tuple_t1 t0) where (
        def to_text: tuple_t1 t0 -> text =
            [ tuple_1 t0 -> 
                t = concat (txt t0) ")";
                t = concat "(" t;
                t
            ]
    )

    instance 
        ::Text t0 ::Text t1 =>
        Text (tuple_t2 t0 t1) where (
        def to_text: tuple_t2 t0 t1 -> text =
            [ tuple_2 t0 t1 -> 
                t = concat (txt t1) ")";
                t = concat ", " t;
                t = concat (txt t0) t;
                t = concat "(" t;
                t
            ]
    )

    instance 
        ::Text t0 ::Text t1 ::Text t2 =>
        Text (tuple_t3 t0 t1 t2) where (
        def to_text: tuple_t3 t0 t1 t2 -> text =
            [ tuple_3 t0 t1 t2 -> 
                t = concat (txt t2) ")";
                t = concat ", " t;
                t = concat (txt t1) t;
                t = concat ", " t;
                t = concat (txt t0) t;
                t = concat "(" t;
                t
            ]
    )

    instance 
        ::Text t0 ::Text t1 ::Text t2 ::Text t3 =>
        Text (tuple_t4 t0 t1 t2 t3) where (
        def to_text: tuple_t4 t0 t1 t2 t3 -> text =
            [ tuple_4 t0 t1 t2 t3 -> 
                t = concat (txt t3) ")";
                t = concat ", " t;
                t = concat (txt t2) t;
                t = concat ", " t;
                t = concat (txt t1) t;
                t = concat ", " t;
                t = concat (txt t0) t;
                t = concat "(" t;
                t
            ]
    )

    instance 
        ::Text t0 ::Text t1 ::Text t2 ::Text t3 ::Text t4 =>
        Text (tuple_t5 t0 t1 t2 t3 t4) where (
        def to_text: tuple_t5 t0 t1 t2 t3 t4 -> text =
            [ tuple_5 t0 t1 t2 t3 t4 -> 
                t = concat (txt t4) ")";
                t = concat ", " t;
                t = concat (txt t3) t;
                t = concat ", " t;
                t = concat (txt t2) t;
                t = concat ", " t;
                t = concat (txt t1) t;
                t = concat ", " t;
                t = concat (txt t0) t;
                t = concat "(" t;
                t
            ]
    )

    instance 
        ::Text t0 ::Text t1 ::Text t2 ::Text t3 ::Text t4 
        ::Text t5 =>
        Text (tuple_t6 t0 t1 t2 t3 t4 t5) where (
        def to_text: tuple_t6 t0 t1 t2 t3 t4 t5 -> text =
            [ tuple_6 t0 t1 t2 t3 t4 t5 -> 
                t = concat (txt t5) ")";
                t = concat ", " t;
                t = concat (txt t4) t;
                t = concat ", " t;
                t = concat (txt t3) t;
                t = concat ", " t;
                t = concat (txt t2) t;
                t = concat ", " t;
                t = concat (txt t1) t;
                t = concat ", " t;
                t = concat (txt t0) t;
                t = concat "(" t;
                t
            ]
    )

    instance 
        ::Text t0 ::Text t1 ::Text t2 ::Text t3 ::Text t4 
        ::Text t5 ::Text t6 =>
        Text (tuple_t7 t0 t1 t2 t3 t4 t5 t6) where (
        def to_text: tuple_t7 t0 t1 t2 t3 t4 t5 t6 -> text =
            [ tuple_7 t0 t1 t2 t3 t4 t5 t6 -> 
                t = concat (txt t6) ")";
                t = concat ", " t;
                t = concat (txt t5) t;
                t = concat ", " t;
                t = concat (txt t4) t;
                t = concat ", " t;
                t = concat (txt t3) t;
                t = concat ", " t;
                t = concat (txt t2) t;
                t = concat ", " t;
                t = concat (txt t1) t;
                t = concat ", " t;
                t = concat (txt t0) t;
                t = concat "(" t;
                t
            ]
    )

    instance 
        ::Text t0 ::Text t1 ::Text t2 ::Text t3 ::Text t4 
        ::Text t5 ::Text t6 ::Text t7 =>
        Text (tuple_t8 t0 t1 t2 t3 t4 t5 t6 t7) where (
        def to_text: tuple_t8 t0 t1 t2 t3 t4 t5 t6 t7 -> text =
            [ tuple_8 t0 t1 t2 t3 t4 t5 t6 t7 -> 
                t = concat (txt t7) ")";
                t = concat ", " t;
                t = concat (txt t6) t;
                t = concat ", " t;
                t = concat (txt t5) t;
                t = concat ", " t;
                t = concat (txt t4) t;
                t = concat ", " t;
                t = concat (txt t3) t;
                t = concat ", " t;
                t = concat (txt t2) t;
                t = concat ", " t;
                t = concat (txt t1) t;
                t = concat ", " t;
                t = concat (txt t0) t;
                t = concat "(" t;
                t
            ]
    )

    instance 
        ::Text t0 ::Text t1 ::Text t2 ::Text t3 ::Text t4 
        ::Text t5 ::Text t6 ::Text t7 ::Text t8 =>
        Text (tuple_t9 t0 t1 t2 t3 t4 t5 t6 t7 t8) where (
        def to_text: tuple_t9 t0 t1 t2 t3 t4 t5 t6 t7 t8 -> text =
            [ tuple_9 t0 t1 t2 t3 t4 t5 t6 t7 t8 -> 
                t = concat (txt t8) ")";
                t = concat ", " t;
                t = concat (txt t7) t;
                t = concat ", " t;
                t = concat (txt t6) t;
                t = concat ", " t;
                t = concat (txt t5) t;
                t = concat ", " t;
                t = concat (txt t4) t;
                t = concat ", " t;
                t = concat (txt t3) t;
                t = concat ", " t;
                t = concat (txt t2) t;
                t = concat ", " t;
                t = concat (txt t1) t;
                t = concat ", " t;
                t = concat (txt t0) t;
                t = concat "(" t;
                t
            ]
    )

    instance 
        Ord tuple_t0 where (
        def compare: tuple_t0 -> tuple_t0 -> int =
            [ tuple_0, tuple_0 -> 0 ]
    )

    instance 
        ::Ord t0 =>
        Ord (tuple_t1 t0) where (
        def compare: tuple_t1 t0 -> tuple_t1 t0 -> int =
            [ tuple_1 t0, tuple_1 t1 -> 
                comp1 t0 t1
            ]
    )

    instance 
        ::Ord t0 ::Ord t1 =>
        Ord (tuple_t2 t0 t1) where (
        def compare: tuple_t2 t0 t1 -> tuple_t2 t0 t1 -> int =
            [ tuple_2 t0 t1, tuple_2 t2 t3 -> 
                comp2 t0 t2 t1 t3
            ]
    )

    instance 
        ::Ord t0 ::Ord t1 ::Ord t2 =>
        Ord (tuple_t3 t0 t1 t2) where (
        def compare: tuple_t3 t0 t1 t2 -> tuple_t3 t0 t1 t2 -> int =
            [ tuple_3 t0 t1 t2, tuple_3 t3 t4 t5 -> 
                comp3 t0 t3 t1 t4 t2 t5
            ]
    )
)

namespace opt (

    using system

    type opt = \=> [ just t | nothing ]

    def nothing_test: opt t -> bool =
        [ nothing -> true
        | _       -> false ]

    def value: opt t -> t =
        [ just x -> x ]
 
    def sequential: (-> opt b) -> (-> opt c) -> (-> opt c) =
        [ f, g, x ->
            [ nothing -> nothing | just y -> g y ] 
                (f x) 
        ]

)

namespace memory (

    using system

    type pointer = { pointer }

    def allocate: int -> pointer =
        [ sz -> {mem.allocate:pointer[sz:int]} ]

    def free: pointer -> unit =
        [ p -> {mem.free:void[p:pointer]} ]

    def sizeof_int: unit -> int =
        [ u -> {mem.sizeof_int:int[u:void]} ]

    def get_int: pointer -> int -> int =
        [ p, n -> {mem.get_int:int[p:pointer, n:int]} ]

    def set_int: pointer -> int -> int -> unit =
        [ p, n, v -> {mem.set_int:int[p:pointer, n:int, v:int]} ]

    def sizeof_char: unit -> int =
        [ u -> {mem.sizeof_char:int[u:void]} ]

    def get_char: pointer -> int -> char =
        [ p, n -> {mem.get_char:char[p:pointer, n:int]} ]

    def set_char: pointer -> int -> char -> unit =
        [ p, n, v -> {mem.set_char:char[p:pointer, n:int, v:char]} ]

    def sizeof_byte: unit -> int =
        [ u -> {mem.sizeof_char:int[u:void]} ]

    def get_byte: pointer -> int -> int =
        [ p, n -> {mem.get_byte:int[p:pointer, n:int]} ]

    def set_byte: pointer -> int -> int -> unit =
        [ p, n, v -> {mem.set_byte:int[p:pointer, n:int, v:int]} ]

    def is_null: pointer -> bool =
        [ p -> {mem.is_null:int[p:pointer]} ]
 


I spotted two bugs just by putting it here, time for that documentation generator.