/**~<<~>>~<<~>>~<<~>>~<<~>>~<<~>>~<<~>>~<<~>>~**
 *           Brief Syntax Definition           *
 *  of the functional language kernel LT-core  *
 *     (please suggest a better name)          *
 **~<<~>>~<<~>>~<<~>>~<<~>>~<<~>>~<<~>>~<<~>>~**
 *   (Placeholders are written in CAPS-LOCK)   *
 **~<<~>>~<<~>>~<<~>>~<<~>>~<<~>>~<<~>>~<<~>>~**
 *
 *
 *   Type-Application
 * ------------------
 * specializes a generic symbol
 * ``
 *     <GENERIC-SYMBOL-NAME TYPE-ARG>
 * ``
 *
 *   Type-Abstraction
 * ------------------
 * creates a generic symbol with a type-variable and optional trait bounds
 * ``
 *     let GENERIC-SYMBOL-NAME = Λ TYPE-ARG-NAME : TYPE-ARG-BOUNDS . VAL-EXPR ;
 * ``
 *
 *   Term-Application
 * ------------------
 * calculates result from applying arguments to a function
 * with prefix-notation and left-associative `(` `)` parens.
 * ``
 *     FUNCTION-NAME ARG-EXPR
 * ``
 *
 *   Term-Abstraction
 * ------------------
 * binds a function definition to a symbol
 * ``
 *     let SYMBOL-NAME = λ ARG-BINDING -> RESULT-BINDING ↦ RESULT-EXPR ;
 * ``
 *
 *   Argument-Binding-Block
 * ------------------------
 * binds variable names to function arguments
 * ``
 *     λ {
 *         ARG-NAME-1 : ARG-TYPE-1 ;
 *         ARG-NAME-2 : ARG-TYPE-2 ;
 *         ... ;
 *         ARG-NAME-N : ARG-TYPE-N ;
 *     }
 * ``
 *
 *   Result-Binding-Block
 * ----------------------
 * binds variable names to function result
 * ``
 *     -> {
 *         RESULT-NAME-1 : RESULT-TYPE-1 ;
 *         RESULT-NAME-2 : RESULT-TYPE-2 ;
 *         ...
 *         RESULT-NAME-N : RESULT-TYPE-N ;
 *     }
 * ``
 *
 *   Construction-Block
 * --------------------
 * builds up result value of the function
 * by sequentially pushing values to the stack
 * ``
 *     ↦ {
 *         VAL-EXPR-N ;
 *         ...
 *         VAL-EXPR-2 ;
 *         VAL-EXPR-1 ;
 *     }
 * ``
 *
 *   Symbol visibility
 * -------------------
 * Symbols defined through `let` are local
 * to the scope of the block they are contained in,
 * but can be made visible in the parent block with `export`.
 *
 * ``
 *     {
 *         export { let x = 5; };
 *         x;
 *     }
 * ``
 *
 *   Explicit Dataflow
 * -------------------
 * write to memory referenced a symbol or expression
 * ``
 *     ! REF-EXPR VAL-EXPR ;
 * ``
 * where
 *   - `REF-EXPR` is an expression of type &T
 *   - `VAL-EXPR` is of type T
 *
 **~<<~>>~<<~>>>~<<~>>~<<<~>>>~<<~>>~<<<~>>>~<<~>>~<<<~>>~<<~>>**
 */


/* Results of a function can be defined either by construction blocks
 * or by statement blocks that write variables bound to the result.
 * The following function definitions of dup'n|n∈ℕ are equivalent:
 */
let dup'0 = λx ↦ { x; x; };
let dup'1 = ΛT . λx:T -> { _:T; _:T; } ↦ { x; x; };
let dup'2 = ΛT . λx:T -> { a:T; b:T; } ↦ { !a x; !b x; };

/* note that construction blocks build up a stack, i.e.
 * the topmost element / the first element of a structure
 * is at the last position (like expressions in postfix).
 */
let swap'0 = ΛT . λ{ x:T; y:T; } ↦ { x; y; };
let swap'1 = ΛT . λ{ x:T; y:T; } -> { yo:T; xo:T } ↦ { !yo x; !xo y; };

/* binding blocks can be curried
 */
let swap'2 = ΛT . λx:T ↦ λy:T ↦ { x; y; };

/* Type-Annotations are possible in many places.
 * With an annotation of a function-type, the types
 * of its argument variables can be deduced.
 */
let swap'3 : { T; T; } -> { T; T; } = ΛT . λ{ x y } ↦ { x; y; };

/*
 * T.F.A.E.:
 */
let fun'1 : {A, B} -> C = λ{a b} ↦ { ... } ;
let fun'2 : A -> B -> C = λ{a b} ↦ { ... } ;
let fun'3 = λ{a: A; b: B;} ↦ C: { ... };
let fun'4 = λ{a: A} ↦ λ{b: B} ↦ C: { ... };

/* Argument-Bindings with a structure-type will
 * result in further (localized) variable bindings
 * according to the struct-type definition.
 */
let vec2.magnitude'0 = λ{ x:ℝ; y: ℝ; } ↦ sqrt (+ (* x x) (* y y)) ;
let vec2.magnitude'1 = λa:{ x:ℝ; y:ℝ; } ↦ sqrt (+ (* a.x a.x) (* a.y a.y)) ;



/* And now more advanced stuff...
 */
let Iterator = ΛItem . trait Self {
	next : &!Self -> <Option Item> ;
};

trait Field F {
    #[infix] + : F -> F -> F ;
    #[infix] - : F -> F -> F ;
    #[infix] * : F -> F -> F ;
    #[infix] / : F -> F -> F ;
};

impl Field for ℝ ~ machine.Float64 {
    let + = machine.Float64.add ;
    let - = machine.Float64.sub ;
    let * = machine.Float64.mul ;
    let / = machine.Float64.div ;
}

impl Field for ℤ_2^64 ~ machine.UInt64 {
    let + = λ{a b} ↦ machine.UInt64.add a b ;
    let - = λ{a b} ↦ machine.UInt64.sub a b ;
    let * = λ{a b} ↦ machine.UInt64.mul a b ;
    let / = λ{a b} ↦ machine.UInt64.div a b ;
};

impl ℤ_2^64 ~ machine.UInt64 {
    let % = machine.UInt64.rem ;
}

let <square F:Field> : F -> F = λ x : F ↦ F:{x * x};



let SeqIter    = ΛT . type { seq: &[T]; idx: ℤ; };
let SeqIterMut = ΛT . type { seq: &![T]; idx: ℤ; };

impl Iterator for <SeqIter T> {
    let next = λ self: &!Self ↦ {
        self.seq.get self.idx;
		!self.idx (self.idx + 1);
	};
};

/* finds the index of the first item in the sequence
 * which satisfies the given predicate.
 */
let seq.position = ΛT . λ{ seq: &[T]; pred: T -> Bool; } -> ℤ ↦ {
    let mut idx : ℤ_2^64 ~ machine.UInt64 = 0;
    while( bit-and
	         (ℤ.lt idx seq.len)
			 (not (pred (seq.get idx)))
    ) {
        !idx (+ idx 1);
    }
    idx;
};

let iter-get-position = ΛT . λ iter: &<Iterator T> ~ &<SeqIter T> ↦ iter.idx ;



#[isomorphism]
let morph-fractpos-point2scale =
    λ x : ℝ
        ~ <FractPosInt 10 LittleEndian>
        ~ [ <Digit 10>+'.' ~ Char ]
        ~ <Vec Char>
      -> ℝ
       ~ <FractPosInt 10 LittleEndian>
       ~ {
          digits: [<Digit 10>~Char] ~ <Vec Char> ;
          scale: ℤ ~ ℤ_2^64 ~ machine.UInt64 ;
       }
    ↦ {
        !scale (digits.position λc ↦ (eq c '.'));
        !digits (digits.filter λc ↦ (not (eq c '.')));
    }
;

#[isomorphism]
let morph-fractpos2float =
    Λ Radix : ℕ
    .λ x : ℝ
        ~ <FractPosInt Radix BigEndian>
        ~ {
            digits: [<Digit Radix>~Char] ~ <Vec Char> ;
            scale: ℤ ;
        }
     -> ℝ
      ~ machine.Float64
    ↦ {
		machine.Float64.div
            ((digits as ℝ~ℕ~<PosInt Radix BigEndian>) as ℝ~machine.Float64)
            ((ℤ.pow Radix scale) as machine.Float64)
        ;
    }
;