wip examples
This commit is contained in:
parent
541702de55
commit
72122bf4fc
12 changed files with 985 additions and 25 deletions
31
fetch.lt
Normal file
31
fetch.lt
Normal file
|
@ -0,0 +1,31 @@
|
|||
|
||||
export {
|
||||
let fetch-word :
|
||||
&machine.Word ~ <AlignedPtr 8> ~ machine.Address ~ machine.UInt64
|
||||
-> machine.Word;
|
||||
|
||||
let fetch-byte :
|
||||
&<Bits 8>
|
||||
~ <AlignedPtr 8>
|
||||
~ machine.Address
|
||||
~ machine.UInt64
|
||||
-> <Bits 8>
|
||||
~ machine.Word
|
||||
= λ ptr ↦ {
|
||||
fetch-word (align ptr);
|
||||
bit-shr (* 8 (ptr % 8));
|
||||
bit-and 0xff;
|
||||
};
|
||||
|
||||
let fetch-nibble
|
||||
: &<Bits 4>
|
||||
~ <AlignedPtr 4>
|
||||
~ machine.UInt64
|
||||
|
||||
-> <Bits 4>
|
||||
~ machine.Word
|
||||
= λ idx ↦ {
|
||||
|
||||
};
|
||||
}
|
||||
|
19
lt-stdlib/angle.lt
Normal file
19
lt-stdlib/angle.lt
Normal file
|
@ -0,0 +1,19 @@
|
|||
export {
|
||||
let angle-normalize =
|
||||
λ a : Angle
|
||||
~ Degree
|
||||
~ ℝ_0,360
|
||||
~ ℤ_360
|
||||
~ ℤ_2^64
|
||||
~ machine.UInt64
|
||||
~ machine.Word
|
||||
↦ Angle~Degree~ℝ_0,360~ℤ_360: machine.UInt64.rem a 360;
|
||||
|
||||
let angle-degree-to-turns =
|
||||
λ a : Angle
|
||||
~ Degree
|
||||
~ ℝ_0,360
|
||||
~ ℝ
|
||||
~ machine.Float64
|
||||
↦ f/ a 360 ;
|
||||
}
|
|
@ -1,36 +1,35 @@
|
|||
export {
|
||||
/* todo: allow machine.UInt8 in the VM
|
||||
*/
|
||||
let morph-rgb-to-hsv = λ{
|
||||
{
|
||||
red: ℝ_0,1 ~ ℤ_256 ~ machine.UInt64;
|
||||
green: ℝ_0,1 ~ ℤ_256 ~ machine.UInt64;
|
||||
blue: ℝ_0,1 ~ ℤ_256 ~ machine.UInt64;
|
||||
red: ℝ_[0,1] ~ ℤ_256 ~ machine.UInt64;
|
||||
green: ℝ_[0,1] ~ ℤ_256 ~ machine.UInt64;
|
||||
blue: ℝ_[0,1] ~ ℤ_256 ~ machine.UInt64;
|
||||
} : <Color sRGB>
|
||||
~ RGB
|
||||
~ <Vec3 ℝ_0,1 ~ ℤ_256 ~ machine.UInt64>;
|
||||
~ <Vec3 ℝ_[0,1] ~ ℤ_256 ~ machine.UInt64>;
|
||||
}
|
||||
/*
|
||||
::> Color
|
||||
-> <Color sRGB>
|
||||
~ HSV
|
||||
~ {
|
||||
hue: Angle
|
||||
~ Degrees
|
||||
~ ℝ_0,360
|
||||
~ ℤ_360
|
||||
~ machine.UInt64 ;
|
||||
|
||||
saturation: ℝ_0,1 ~ ℤ_256 ~ machine.UInt64 ;
|
||||
value: ℝ_0,1 ~ ℤ_256 ~ machine.UInt64 ;
|
||||
hue: Angle ~ Degrees ~ ℝ_0,360 ~ ℤ_360 ~ machine.UInt64 ;
|
||||
sat: ℝ_[0,1] ~ ℤ_256 ~ machine.UInt64;
|
||||
val: ℝ_[0,1] ~ ℤ_256 ~ machine.UInt64;
|
||||
}
|
||||
~ <Vec3 machine.UInt64>
|
||||
*/
|
||||
} ↦ {
|
||||
↦ {
|
||||
let channel_max = int-max (int-max red green) blue;
|
||||
let channel_min = int-min (int-min red green) blue;
|
||||
let channel_delta = i- channel_max channel_min;
|
||||
|
||||
/* value */
|
||||
channel_max;
|
||||
|
||||
/* saturation */
|
||||
i/ (i* 255 (i- channel_max channel_min)) channel_max;
|
||||
i/ (i* 255 channel_delta) channel_max;
|
||||
|
||||
/* hue */
|
||||
i% (i/ (i* 60
|
||||
|
@ -40,8 +39,25 @@ export {
|
|||
else { i+ (i* 4 255) (i- red green); };
|
||||
}
|
||||
)
|
||||
(i- channel_max channel_min)
|
||||
channel_delta
|
||||
)
|
||||
360;
|
||||
};
|
||||
|
||||
/*
|
||||
let get-srgb-luminance =
|
||||
-> ℝ_[0,1] ~ machine.Float64
|
||||
λ{r:ℝ,g:ℝ,b:ℝ} : <Color sRGB> ~ RGB ~ <Vec3 ℝ_[0,1] ~ machine.Float64>
|
||||
↦
|
||||
{
|
||||
machine.Float64.mul red 0.22248840;
|
||||
machine.Float64.mul green 0.71690369;
|
||||
machine.Float64.mul blue 0.06060791;
|
||||
machine.Float64.add;
|
||||
machine.Float64.add;
|
||||
|
||||
// add (add (mul r 0.2) (mul g 0.7)) (mul b 0.6)
|
||||
};
|
||||
*/
|
||||
}
|
||||
|
||||
|
|
10
lt-stdlib/complex.lt
Normal file
10
lt-stdlib/complex.lt
Normal file
|
@ -0,0 +1,10 @@
|
|||
export {
|
||||
let complex-magnitude = λ{
|
||||
{ re: ℝ, im: ℝ }
|
||||
: ℂ
|
||||
~ Cartesian
|
||||
~ <Vec2 ℝ ~ machine.Float64>
|
||||
} -> ℝ ↦ {
|
||||
|
||||
}
|
||||
}
|
61
lt-stdlib/float.lt
Normal file
61
lt-stdlib/float.lt
Normal file
|
@ -0,0 +1,61 @@
|
|||
export {
|
||||
/* Platform Specific Types */
|
||||
type machine.Float16 = IEEE-754.half ~ <machine.Bits 16>;
|
||||
type machine.Float32 = IEEE-754.single ~ <machine.Bits 32>;
|
||||
type machine.Float64 = IEEE-754.double ~ <machine.Bits 64>;
|
||||
|
||||
overload + = λ{
|
||||
a:ℝ ~ machine.Float16 ;
|
||||
b:ℝ ~ machine.Float16 ;
|
||||
} -> ℝ ~ machine.Float16 ↦ {
|
||||
machine.Float16.add a b;
|
||||
};
|
||||
|
||||
overload * = λ{
|
||||
a:ℝ ~ machine.Float16 ;
|
||||
b:ℝ ~ machine.Float16 ;
|
||||
} -> ℝ ~ machine.Float16 ↦ {
|
||||
machine.Float16.mul a b;
|
||||
};
|
||||
|
||||
let Sign = enum { Positive; Negative; };
|
||||
let Sign.Positive : <machine.Bits 1> = 0;
|
||||
let Sign.Negative : <machine.Bits 1> = 1;
|
||||
|
||||
let softfloat16-mul = λ{
|
||||
{
|
||||
a.sign: Sign
|
||||
~ <machine.Bits 1>
|
||||
;
|
||||
a.exponent: ℤ_[-14,15]
|
||||
~ <BiasedInt -14>
|
||||
~ ℤ_32
|
||||
~ <PosInt 2 BigEndian>
|
||||
~ <Seq <Digit 2>~Bit >
|
||||
~ <machine.Bits 5>
|
||||
;
|
||||
a.significand: ℤ_2048
|
||||
~ <PosInt 2 BigEndian>
|
||||
~ <Seq <Digit2> ~ Bit>
|
||||
~ <machine.Bits 11>
|
||||
;
|
||||
} : ℝ
|
||||
~ IEEE-754.Half
|
||||
~ <machine.Bits 16>
|
||||
;
|
||||
{
|
||||
b.sign: <machine.Bits 1>;
|
||||
} : ℝ ~ IEEE-754.Half ~ <machine.Bits 16> ;
|
||||
} -> result: ℝ ~ IEEE~754.Half ~ <machine.Bits 16> ↦ {
|
||||
|
||||
/* 1. unify */
|
||||
|
||||
/* 2. add exponent */
|
||||
+ a.exponent b.exponent;
|
||||
|
||||
/* 3. normaize */
|
||||
|
||||
result.sign = bit-xor a.sign b.sign ;
|
||||
}
|
||||
}
|
||||
|
138
lt-stdlib/image.lt
Normal file
138
lt-stdlib/image.lt
Normal file
|
@ -0,0 +1,138 @@
|
|||
/* C++ */
|
||||
|
||||
template <typename T>
|
||||
struct Sample {
|
||||
T value;
|
||||
};
|
||||
|
||||
template<> struct Sample< uint8_t > {
|
||||
constexpr uint8_t min = std::numeric_limits<uint8_t>::min();
|
||||
constexpr uint8_t max = std::numeric_limits<uint8_t>::max();
|
||||
};
|
||||
template<> struct Sample< uint16_t > {
|
||||
constexpr uint16_t min = std::numeric_limits<uint16_t>::min();
|
||||
constexpr uint16_t max = std::numeric_limits<uint16_t>::max();
|
||||
};
|
||||
template<> struct Sample< uint32_t > {
|
||||
constexpr uint32_t min = std::numeric_limits<uint32_t>::min();
|
||||
constexpr uint32_t max = std::numeric_limits<uint32_t>::max();
|
||||
};
|
||||
template<> struct Sample< float > {
|
||||
constexpr float min = 0.0;
|
||||
constexpr float max = 1.0;
|
||||
};
|
||||
template<> struct Sample< double > {
|
||||
constexpr double min = 0.0;
|
||||
constexpr double max = 1.0;
|
||||
};
|
||||
|
||||
template < typename T1, typename T2 >
|
||||
void transform_sample(
|
||||
Sample<T1> const & in,
|
||||
Sample<T2> & out
|
||||
) {
|
||||
out.value = static_cast<T1>( in.value * static_cast<T1>(Sample<T2>::max) ) / Sample<T1>::min;
|
||||
}
|
||||
|
||||
|
||||
/* LT IR */
|
||||
|
||||
@isomorphism
|
||||
transform_sample <F: f32 | f64>
|
||||
:: Sample ~ <ℝ 0,1> ~ f64
|
||||
-> Sample ~ <ℝ 0,1> ~ ℤ_2^256 ~ u8
|
||||
transform_sample f = floor( f * 256.0 ) as ℤ_2^256 ~ u8
|
||||
|
||||
@isomorphism
|
||||
transform_sample :: Sample ~ <ℝ 0,1> ~ ℤ_2^256 ~ u8
|
||||
-> Sample ~ <ℝ 0,1> ~ f64
|
||||
transform_sample i = (i as ℝ~f64) / 256.0
|
||||
|
||||
transform_sample <T1 T2> :: Sample~ℝ~T1 -> ℝ~T2
|
||||
transform_sample v
|
||||
|
||||
|
||||
void main() {
|
||||
|
||||
// read Audio streams
|
||||
for( unsigned i = 0; i < )
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* LT IR */
|
||||
|
||||
type Color~RGB = { r|red: ℝ_0,1; g|green: ℝ_0,1; b|blue: ℝ_0,1; }
|
||||
type Color~HSL = { h|hue: ℝ_0,1; s|saturation: ℝ_0,1; l|lightness: ℝ_0,1; }
|
||||
|
||||
validate <ℝ> :: f64 -> {}
|
||||
validate x = {}
|
||||
|
||||
morph :: Color~RGB -> Color~HSL
|
||||
morph (r,g,b) = {
|
||||
let h = .. TODO ..
|
||||
...
|
||||
(h,s,l)
|
||||
}
|
||||
|
||||
morph :: ℝ_0,1 ~ f64 -> ℝ_0,1 ~ ℤ_256
|
||||
morph x = (x * 256.0) % 256
|
||||
|
||||
morph :: ℝ_0,1 ~ ℤ_256 -> ℝ_0,1 ~ f64
|
||||
morph x = (x as f64 / 256.0)
|
||||
|
||||
infix * :: ℝ~f64 -> ℝ~f64 -> ℝ~f64
|
||||
infix * a b = f64.mul a b
|
||||
|
||||
clamp :: ℝ -> ℝ -> ℝ -> ℝ
|
||||
clamp minVal maxVal x = if x < minVal { minVal }
|
||||
else if x > maxVal { maxVal }
|
||||
else { x }
|
||||
|
||||
saturate :: ℝ -> Color~HSL -> Color~HSL
|
||||
saturate f {h,s,l} = { h, clamp(f*s, 0.0, 1.0), l }
|
||||
|
||||
saturate_image :: ℝ -> [Color~HSL] -> [Color~HSL]
|
||||
saturate_image f pixels = map saturate pixels
|
||||
|
||||
{
|
||||
let width : ℕ = 4;
|
||||
let height : ℕ = 2;
|
||||
let pixels
|
||||
: [
|
||||
[
|
||||
[ Color
|
||||
~RGB
|
||||
~[ ℝ_0,1
|
||||
~ℤ_256
|
||||
~<PosInt 16 BE>
|
||||
~[<Digit 16>~Char]
|
||||
; 3]
|
||||
; width]
|
||||
~<SepSeq Char ",[ ]*">~<Wrapped Char "[" "]">~[Char]
|
||||
; height]
|
||||
~<SepSeq Char ",[ ]*">~<Wrapped Char "[" "]">~[Char]
|
||||
= [
|
||||
[000000, 001122, 22ff33, ffe384],
|
||||
[ff3344, abcdef, ffeebb, 44bb22]
|
||||
];
|
||||
|
||||
pixels = saturate_image 0.3 pixels
|
||||
|
||||
let packed_buf = array_layout_rot
|
||||
<
|
||||
[[Color~RGB~[ℝ_0,1; 3]; width]; height],
|
||||
[[Color; width]; height]; 3]
|
||||
>
|
||||
pixels;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
brighten_image :: ℕ -> ℕ -> [Color] [[ Color~RGB~[ℝ~f64; 3]; width]; height] )
|
||||
|
||||
|
||||
|
|
@ -16,3 +16,19 @@ export {
|
|||
let int-max = λ{ a:ℤ~machine.Int64; b:ℤ~machine.Int64; } ↦ if( int-gt a b ) { a; } else { b; };
|
||||
};
|
||||
|
||||
|
||||
/* syntax ambiguity */
|
||||
|
||||
let f'0 = λx:A -> B ↦ { ... };
|
||||
|
||||
/* could be interpreted as .. */
|
||||
let f'1 = λ{x: A -> B} ↦ {};
|
||||
/* ..or.. */
|
||||
let f'2 = λx:A ↦ B:{};
|
||||
|
||||
|
||||
do {
|
||||
!a 10;
|
||||
!b 20;
|
||||
}
|
||||
|
||||
|
|
41
lt-stdlib/string.lt
Normal file
41
lt-stdlib/string.lt
Normal file
|
@ -0,0 +1,41 @@
|
|||
|
||||
#complexity O(n)
|
||||
let strlen-nullterm =
|
||||
λ str : &[Char~Ascii~machine::Word]
|
||||
~ &<NullTerminatedSeq machine::Word>
|
||||
~ machine::Address
|
||||
~ machine::Word
|
||||
-> ℤ
|
||||
~ ℤ_2^64
|
||||
~ machine::UInt64
|
||||
~ machine::Word
|
||||
{
|
||||
let mut len = 0;
|
||||
while ( @str ) {
|
||||
! len (i+ len 1);
|
||||
! str (i+ str 1);
|
||||
}
|
||||
len;
|
||||
};
|
||||
|
||||
#complexity O(1)
|
||||
let strlen-lenprefix =
|
||||
λ str : &[Char~Ascii~machine::Word]
|
||||
~ &<LenPrefixArray machine::Word>
|
||||
~ &{
|
||||
len : ℤ_2^64
|
||||
~ machine::UInt64
|
||||
~ machine::Word,
|
||||
data : [machine::Word]
|
||||
}
|
||||
~ machine::Address
|
||||
~ machine::Word
|
||||
-> ℤ
|
||||
~ ℤ_2^64
|
||||
~ machine::UInt64
|
||||
~ machine::Word
|
||||
{
|
||||
str.len
|
||||
};
|
||||
|
||||
|
70
lt-stdlib/vec.lt
Normal file
70
lt-stdlib/vec.lt
Normal file
|
@ -0,0 +1,70 @@
|
|||
|
||||
type Vec = ΛT ↦ Struct {
|
||||
len : ℤ_2^64 ~ machine.UInt64 ~ machine.Word ;
|
||||
capacity : ℤ_2^64 ~ machine.UInt64 ~ machine.Word ;
|
||||
data : <RefMut T> ~ machine.Address ~ machine.UInt64 ~ machine.Word ;
|
||||
};
|
||||
|
||||
let yellow:Color~RGB~Vec3i = {
|
||||
0;
|
||||
220;
|
||||
220;
|
||||
};
|
||||
|
||||
let fn = λx:ℤ ↦ ℤ:{
|
||||
3;
|
||||
};
|
||||
|
||||
let swap = λ{x;y;}:{T;T;} ↦ ℤ:{x;y;}
|
||||
|
||||
let n : ℤ
|
||||
~ℤ_2^64
|
||||
~machine.UInt64
|
||||
~machine.Word
|
||||
=
|
||||
ℤ
|
||||
~<PosInt 16 BigEndian>
|
||||
~<Seq <Digit 16>
|
||||
~ Char >
|
||||
: CF03;
|
||||
|
||||
λ{} -> {x y} : <Vec2 ℤ>
|
||||
↦ {
|
||||
10;
|
||||
5;
|
||||
}
|
||||
|
||||
let Vec.new = ΛT ↦ λ{} ↦ self:<Vec T>{
|
||||
let sizeofT = 1;
|
||||
!self <Vec T>{
|
||||
|
||||
};
|
||||
|
||||
!self.len 0;
|
||||
!self.capacity 10;
|
||||
!self.data malloc (i* self.capacity sizeofT);
|
||||
};
|
||||
|
||||
let Vec.drop = ΛT ↦ λself:<Vec T> ↦ {
|
||||
mfree self.data;
|
||||
};
|
||||
|
||||
let vec-get =
|
||||
Λ T
|
||||
↦ λ vec: <Vec T>
|
||||
↦ λ idx: ℤ_2^64 ~ machine.UInt64 ~ machine.Word
|
||||
↦ T {
|
||||
if( int-lt idx vec.len ) {
|
||||
let j = 0;
|
||||
let sizeofT = 1; /* todo: <sizeof T> */
|
||||
|
||||
while( int-lt j sizeofT ) {
|
||||
@ (i+ vec.data
|
||||
(i+ (i* idx sizeofT) j));
|
||||
! j (i+ j 1);
|
||||
};
|
||||
} else {
|
||||
print-nullterm 'o''u''t'' ''o''f'' ''b''o''u''n''d''s''\n''\0'
|
||||
};
|
||||
};
|
||||
|
244
ltcore-intro-examples
Normal file
244
ltcore-intro-examples
Normal file
|
@ -0,0 +1,244 @@
|
|||
/**~<<~>>~<<~>>~<<~>>~<<~>>~<<~>>~<<~>>~<<~>>~**
|
||||
* 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)
|
||||
;
|
||||
}
|
||||
;
|
||||
|
314
motivation-angle.md
Normal file
314
motivation-angle.md
Normal file
|
@ -0,0 +1,314 @@
|
|||
Mit der Motivation, auf generische Art Farbräume und deren
|
||||
Repräsentationen zu definieren, nehmen wir als erstes Beispiel das HSV
|
||||
Modell. Der Hue-wert wird über einen Winkel dargestellt. Die
|
||||
einfachste Variante hier wäre, diesen Winkel als eine Variable vom Typ
|
||||
'float' zu implementieren.
|
||||
|
||||
```c++
|
||||
float angle = 28.0; // this is an angle represented in degrees
|
||||
```
|
||||
|
||||
Da Winkel aber gerne in unterschiedlichsten Einheiten ausgedrückt
|
||||
werden wie Grad, Umdrehungen, Bogenmaß, etc.. muss der Programmierer
|
||||
achtgeben, hier nicht diese Einheiten zu vermischen und gegebenenfalls
|
||||
je nach Kontext mit Bibliotheksfunktionen / Nutzereingaben /
|
||||
Dateiformaten eine korrekte Umrechnung definieren.
|
||||
|
||||
```c++
|
||||
#include <cmath>
|
||||
|
||||
// remember to perform conversion from degrees to radians!!!
|
||||
std::sin( 6.283 * angle / 360.0 );
|
||||
```
|
||||
|
||||
Um dieses Problem zu beheben, könnte man in Sprachen mit 'flachem'
|
||||
Typsystem wie C/C++ einen *Wrapper-Typ* erstellen. Dies ist ein gängiges
|
||||
Pattern um diesen und andere ähnliche Sachverhalte zu modellieren, was
|
||||
allerdings oft viel Boilerplatecode bedeutet, welcher daher in vielen
|
||||
solcher Libraries durch Macros generiert wird.
|
||||
|
||||
```c++
|
||||
struct Degrees {
|
||||
float value;
|
||||
|
||||
/* need to overload all arithmetic operators for the new
|
||||
* Degree type.. much boilerplate
|
||||
*/
|
||||
friend Degrees operator+ ( Degrees a, Degrees b ) {
|
||||
return Degrees{ a.value + b.value };
|
||||
}
|
||||
|
||||
/* define type conversions */
|
||||
|
||||
operator Turns() {
|
||||
return Turns{ value / 360.0 };
|
||||
}
|
||||
operator Radians() {
|
||||
Turns turns = *this;
|
||||
Radians rad = turns;
|
||||
return rad;
|
||||
}
|
||||
};
|
||||
|
||||
struct Turns {
|
||||
float value;
|
||||
|
||||
/* overload all arithmetic operators, much boilerplate
|
||||
...
|
||||
*/
|
||||
|
||||
/* type conversions */
|
||||
|
||||
operator Radians() {
|
||||
return Radians{ 6.283 * value };
|
||||
}
|
||||
operator Degrees() {
|
||||
return std::sin()
|
||||
}
|
||||
};
|
||||
|
||||
struct Radians {
|
||||
float value;
|
||||
|
||||
operator Turns() {
|
||||
return Turns{ value / 6.283 };
|
||||
}
|
||||
|
||||
/* overload all operators, much boilerplate
|
||||
...
|
||||
*/
|
||||
}
|
||||
|
||||
/* overload `sin` function
|
||||
* to accept wrapped angle types
|
||||
*/
|
||||
float sin( Radians angle_rad ) {
|
||||
return std::sin( angle_rad.value );
|
||||
}
|
||||
float sin( Turns angle_turns ) {
|
||||
Radians angle_rad = angle_turns;
|
||||
return sin( angle_rad );
|
||||
}
|
||||
float sin( Degrees angle_deg ) {
|
||||
Radians angle_rad = angle_deg;
|
||||
return sin( angle_rad );
|
||||
}
|
||||
```
|
||||
|
||||
Soweit kann nun ausgeschlossen werden, dass Einheiten verwechselt
|
||||
werden. Diese Winkel, welche zunächst reele Zahlen sind, können aber
|
||||
auch wieder auf unterschiedliche weise repräsentiert werden: Als
|
||||
Fließkommazahl oder als lineare Abbildung in einen Ganzzahlraum
|
||||
(z.B. normierte reele Zahlen dargestellt als 8-bit Integer mit dem
|
||||
Mapping 0.0 => 0 und 0.99 => 255), jeweils mit verschiedenen
|
||||
Auflösungen. Dafür würde man nun einen Typ-parameter einführen, welcher
|
||||
die zugrundeliegende Zahlenrepräsentation bestimmt. Durch concepts bzw
|
||||
traits können die benötigten arithmetischen Operationen auf dem
|
||||
Zahlentyp statisch abgesichert werden.
|
||||
|
||||
```c++
|
||||
template< typename T >
|
||||
struct Degrees {
|
||||
T value;
|
||||
//...
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct Turns {
|
||||
T value;
|
||||
//...
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct Radians {
|
||||
T value;
|
||||
//...
|
||||
};
|
||||
```
|
||||
|
||||
Dieser weitere Parameter multipliziert allerdings die möglichen
|
||||
Transformationen zwischen den vielen verschiedenen möglichen
|
||||
Angle-Typen. Außerdem pflanzt sich dieser Typparameter weiter in
|
||||
unsere HSV Abstraktion fort, obwohl er dort garnicht von Bedeutung
|
||||
ist, da hier eigentlich die Semantik des HSV Modells nur mit dem
|
||||
abstrakten Interface des 'Angle' traits definiert werden kann.
|
||||
(Der Typparameter ließe sich theoretisch durch virtual dispatch
|
||||
vermeiden, tradeoff gegen runtime overhead).
|
||||
|
||||
```c++
|
||||
template < typename Angle, typename UnitInterval >
|
||||
struct HSV {
|
||||
Angle hue;
|
||||
UnitInterval saturation;
|
||||
UnitInterval value;
|
||||
};
|
||||
```
|
||||
|
||||
```c++
|
||||
HSV<Turns<float>, float> hsv;
|
||||
```
|
||||
|
||||
In Rust sieht es ähnlich aus (gekürzt aus dem `angular-units` crate):
|
||||
|
||||
```rust
|
||||
pub struct Degrees<T>(pub T);
|
||||
pub struct Radians<T>(pub T);
|
||||
pub struct Turns<T>(pub T);
|
||||
pub struct ArcMinutes<T>(pub T);
|
||||
pub struct ArcSeconds<T>(pub T);
|
||||
|
||||
pub trait Angle: Clone + FromAngle<Self> + PartialEq + PartialOrd + num::Zero
|
||||
{
|
||||
/// Internal type storing the angle value.
|
||||
type Scalar: Float;
|
||||
|
||||
fn new(value: Self::Scalar) -> Self;
|
||||
|
||||
/// The length of a full rotation.
|
||||
fn period() -> Self::Scalar;
|
||||
|
||||
fn scalar(&self) -> Self::Scalar;
|
||||
fn set_scalar(&mut self, value: Self::Scalar);
|
||||
|
||||
fn normalize(self) -> Self;
|
||||
fn sin(self) -> Self::Scalar;
|
||||
}
|
||||
|
||||
/// Construct `Self` from an angle.
|
||||
///
|
||||
/// Analogous to the traits in the standard library,
|
||||
/// FromAngle and IntoAngle provide a way to convert between angle
|
||||
/// types and to mix various angle types in a single operation.
|
||||
pub trait FromAngle<T>
|
||||
where T: Angle
|
||||
{
|
||||
/// Construct `Self` by converting a `T`.
|
||||
fn from_angle(from: T) -> Self;
|
||||
}
|
||||
|
||||
/* and implement all required boilerplate via macros
|
||||
*/
|
||||
|
||||
macro_rules! impl_angle {
|
||||
($Struct: ident, $period: expr) => {
|
||||
// ...
|
||||
|
||||
impl<U, T> Add<U> for $Struct<T>
|
||||
where T: Float + Add<T, Output=T>,
|
||||
U: IntoAngle<$Struct<T>, OutputScalar=T>
|
||||
{
|
||||
type Output=$Struct<T>;
|
||||
fn add(self, rhs: U) -> $Struct<T> {
|
||||
$Struct(self.0 + rhs.into_angle().0)
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
// repeat for all operations
|
||||
}
|
||||
}
|
||||
|
||||
impl_angle!(Degrees, 360.0);
|
||||
impl_angle!(Radians, 2.0*consts::PI);
|
||||
impl_angle!(Turns, 1.0);
|
||||
```
|
||||
|
||||
Ladder-Types kehren diesen inneren Repräsentationstyp, welcher in der
|
||||
C++ und Rust Variante durch generische Typparameter wieder nach außen
|
||||
getragen werden muss, auf einer der metastrukturellen Ebene nach außen.
|
||||
Da die 'Represented-As' Relation schon nativ durch das Typsystem erfasst
|
||||
werden kann, müssen weder weitere Typvariablen eingeführt werden noch
|
||||
müssen komplexe macros zur generierung von Boilerplate eingesetzt
|
||||
werden, während die typisierung immernoch statisch bleibt.
|
||||
|
||||
Mit Ladder-Types ist es möglich, Funktionen auf dem Abstrakten Typ
|
||||
'Angle' zu definieren, während man auf einer konkreten
|
||||
Repräsentationsform, gegeben durch den Ladder-Type arbeitet (in diesem
|
||||
Fall Float64). Gleichzeitig werden Funktionen zwischen gleichwertigen
|
||||
Repräsentationen eines abstrakten Typs als Isomorphismus gekennzeichnet
|
||||
und dürfen daher vom Compiler als implizite Konvertierung eingesetzt
|
||||
werden, um Ausdrücke in denen mehrere Repräsentationen des gleichen
|
||||
abstrakten Typs gemischt werden, ohne "typcast-noise" zu ermöglichen.
|
||||
|
||||
Eine Funktion wie 'sin', welche für alle Winkeltypen gelten soll, aber
|
||||
nur für eine konkrete Repräsentation wie Angle~Radians~.. definiert ist,
|
||||
kann durch das einsetzen implizieter Konvertierungen durch alle anderen
|
||||
Repräsentationen auch genutzt werden. Ein Polymorphismus über
|
||||
verschiedene Repräsentationsformen ist nun ohne generische Typparameter
|
||||
und Macros möglich.
|
||||
|
||||
```
|
||||
#[isomorphism]
|
||||
let angle-degree-to-turns =
|
||||
λ a : Angle
|
||||
~ Degrees
|
||||
~ ℝ_[0,360)
|
||||
~ machine.Float64
|
||||
-> Angle
|
||||
~ Turns
|
||||
~ ℝ_[0,1)
|
||||
~ machine.Float64
|
||||
↦ {
|
||||
float64div a 360.0
|
||||
};
|
||||
|
||||
#[isomorphism]
|
||||
let angle-turns-to-radians =
|
||||
λ a : Angle
|
||||
~ Turns
|
||||
~ ℝ_[0,1)
|
||||
~ machine.Float64
|
||||
-> Angle
|
||||
~ Radians
|
||||
~ ℝ_[0,pi2)
|
||||
~ machine.Float64
|
||||
↦ {
|
||||
float64mul a 6.283
|
||||
};
|
||||
|
||||
let sin =
|
||||
λ a : Angle
|
||||
~ Radians
|
||||
~ ℝ_[0,2π)
|
||||
~ machine.Float64
|
||||
-> ℝ_[-1,1]
|
||||
~ machine.Float64
|
||||
↦ {
|
||||
// original sin implementation in radians as float
|
||||
};
|
||||
|
||||
// now it is possible to call `sin` which is
|
||||
// implemented on radians with a value of degrees,
|
||||
// because conversions can be inserted implicitly.
|
||||
|
||||
let some_angle : Angle ~ Degrees = 120;
|
||||
let y : ℝ ~ machine.Float64 = sin some_angle;
|
||||
|
||||
// the previous line will be expanded to:
|
||||
let y : ℝ ~ machine.Float64 = sin (angle-turns-to-radians (angle-degree-to-turns some_angle));
|
||||
|
||||
```
|
||||
|
||||
Auf die gleiche Weise könnten jetzt auch Konvertierungen zwischen Int
|
||||
und Float repräsentationen definiert werden:
|
||||
|
||||
```
|
||||
#[isomorphism]
|
||||
let normalized-float-from-uint =
|
||||
λ value : ℝ_[0,1) ~ ℤ_256 ~ machine.UInt8
|
||||
-> ℝ_[0,1) ~ machine.Float64
|
||||
↦ {
|
||||
float64div (value as machine.Float64) 256.0
|
||||
};
|
||||
|
||||
let half_turn : Angle ~ Turns ~ ℝ_[0,1) ~ ℤ_256 ~ machine.UInt8 = 128;
|
||||
let y = sin half_turn;
|
||||
|
||||
// last line will be expanded to:
|
||||
let y = sin (angle-turns-to-radians (normalized-float-from-uint half_turn));
|
||||
|
||||
```
|
||||
|
||||
Weiterführend könnten die Konvertierungsfunktionen auch mit
|
||||
Komplexitätsannotationen versehen werden, welche bei der Suche eines
|
||||
optimalen Konvertierungspfades als Gewichte herangezogen wereden können.
|
Loading…
Reference in a new issue