- a simple functional language
Syntax example
-- facc n computes n!
facc x | x == 0 = 1
| x > 0 = x * facc (x - 1);
- comments are single-line only and must be prefixed with
-- - guards can also be omitted entirely
- n-ary functions already take n-tuples
f (a1, ..., an)(sof x = f (x)) ;must separate each definition; from a design standpoint this is there so that we can easily ignore any whitespace character- infix operators are always left-associative;
wherehas the lowest priority, then+,-,or, then*,/,and, andnotand unary minus (e.g.-5) have the highest priority. Both boolean and functional expressions can be parenthesized as needed, after which the priorities are clear. - all arithmetic operations are evaluated over ints (except
/, which is integer division).
For simplicity, there are several things that Husky does not guarantee:
- function order does not matter
- inside
whereblocks, you can only refer to the formal parameters of the "parent" function - you cannot nest multiple
whereblocks (i.e. usewhereblocks insidewhereblocks) - guards must be "mutually exclusive", because the order in which they are evaluated is not guaranteed!
- if a function is non-total, i.e. there is a case for a formal parameter that isn't covered by any of the guards, then Husky does not guarantee correctness or that an error will be thrown — 0 is returned instead
- since output happens only via the exit code, the sign is unfortunately lost on output — this doesn't mean, however, that it isn't handled internally
whereblocks should not override any other local definition; they may only override global definitions (shadowing works)
Husky code is not optimized and relies on LLVM optimizations, i.e.:
- even without guards, a jump is inserted, which is trivially true and jumps into the if branch
- the same instructions are inserted into different unnamed variables; LLVM can largely eliminate this
- replace
StringwithByteStringeverywhere (including in the lexer, where a wrapperbytestring-monadis needed) - rework local definitions to use
AddrSpace - improve the AST types, and especially the traversal in the emitter, so that it isn't done via recursion but via catamorphisms
- replace arrays with maps, especially in
NameTable, etc. - get rid of unnecessary duplicate computation of the same thing — in several places something is done twice (mainly in the emitter)