Bee Types
Type System:
-
Bee is using a gradual typing system. We think dynamic typing is more fragile and prone to errors. Therefore Bee is a strongly typed language with type inference. That means any data is constrained by fixed "data type" rules.
-
Bee has predefined data types. You can also create new data types based on predefined types using a type declaration. You can create sub-types or composite types having new constraints and rules that can improve data validation further.
-
A data type can be manipulated using rules and operators. You can have a variable of type: Type. To detect a data type of any variable you can use the introspection function kind() that will be defined later.
Primitive Types
Primitive data types are defined using one capital letter. This may be unusual in Computer Science but standard in Mathematics. Therefore Bee uses this convention. We should have used Unicode symbols but we have decided to use simplest solution. Latin uppercase characters therefore are reserved. Bee is case sensitive.
| Alias | Code | Description + Default representation |
|---|---|---|
| Boolean | B | Logic value: 0 = false, 1 = true. Default 0b0. |
| Alpha | A | Printable 7-bit ASCII character (32..126). Default '0'. |
| Rune | U | Full Unicode code point (4 bytes), incl. control/unprintable. Default U+0000. |
| Natural | N | Non-negative integer ℕ₀ [0 .. 2^64-1], 64-bit unsigned. Default 0. |
| Integer | Z | Signed integer ℤ [-2^63 .. 2^63-1], 64-bit 2's complement. Default 0. |
| Real | R | Double-precision IEEE 754 float (8 bytes). Default 0.0. |
| Rational | Q | Fixed-point fraction Q(m.n), e.g. 1\2. Default 0\1. |
| Complex | C | Pair of Reals a + bj (16 bytes). Default 0.0 + 0.0j. |
| String | S | Immutable UTF-8 string. Default "". |
| Date | D | Gregorian date (day, month, year). Default 01/01/1970. |
| Time | T | 24-hour time (h, m, s, ms). Default 00:00:00. |
| Angular | G | Geometric angle 1°..360°, fixed 16-bit float. Default 0.0°. |
| Lambda | L | Higher-order function type, a reference to a pure lambda expression. Default λ. |
Notes:
- Each data type has a default representation for literal, print & output,
- Output representation can be establish using a format template,
- Using print with primitive type will create a specific representation,
- Precision can be specified in parenthesis after the type: Z(32) = 32 bit integer,
- Primitive data types are values allocated on the stack or in the registry.
Constant Literals
These are symbolic representations for primitive data types:
| Example | Type | Literal characters |
|---|---|---|
| 'a' | A | printable ASCII (32..126) |
| 'Ω' | U | (Δ Λ Φ Γ Ψ Ω Σ Π π ⊥ ǁ α β ɣ ε δ μ ω ...) |
| "str" | S | (∀ UTF8) |
| 0b0,0b1 | B | (0,1) & B |
| 1234567890 | N | (0,1,2,3,4,5,6,7,8,9) |
| +0 | Z | (-+) & (0,1,2,3,4,5,6,7,8,9) |
| 'a','b' | A | printable ASCII (0x20..0x7E) |
| U+FFFF | U | (U+) & (0,1,2,3,4,5,6,7,8,9) & ABCDEF |
| U-FFFFFFFF | U | (U-) & (0,1,2,3,4,5,6,7,8,9) & ABCDEF |
| 0.05 | R | (-.) & (0,1,2,3,4,5,6,7,8,9) |
| -1\2 | Q | (-\) & (0,1,2,3,4,5,6,7,8,9) |
| 1E10 | R | (-1E)& (0,1,2,3,4,5,6,7,8,9) |
| 1e10 | R | (-1e)& (0,1,2,3,4,5,6,7,8,9) |
note
- primitive types are ordered and can be compared,
- primitive literals are identical to themselves,
- primitive variables are identical to themselves.
- primitive types can be used to define subtypes or composite types
Named Complex Types
Complex types are identified by name, not by a reserved single uppercase letter. The collection types (Array, Map, List) are listed below; the remaining named complex types are:
| Type | Symbol | Description |
|---|---|---|
| Graph | Graph | Named collection of vertices and edges. |
Collection types
Bee define a collection literal using a special notation based on brackets. These types are going to be explained later in another page. We mention these types here because they represent types.
| delimiter | collection types |
|---|---|
| () | List |
| [] | Array / Matrix |
| {} | Set / Map / Object / Ordinal |
Notes:
- All collections are actually references,
- Collection elements can be references or native types,
- Elements of collection are separated by comma,
- Pair of values are separated by columns ":"
Type declaration
User can define type alias using operators ":" and sub-types using operator "<:" (inheritance). Usr can specify type of variables explicit using operator ∈
-- declare new type alias
type Type_Identifier: type_descriptor <: super_type;
-- declare variables using type alias
new var_name ∈ Type_Identifier;
-- declare many variables using type alias
new var_name, var_name ... ∈ Type_Identifier;
Notes:
- User defined types are starting with capital letter;
- User defined super-types are usually composite types;
- User defined sub-types are usually domains of values;
Optional Types & the nil sentinel
An optional type T? is a union that adds the nil sentinel to T: the value is either a T or the absence of a value (nil). It is written as a postfix ? on a typed member or reference.
-- an optional reference: a Node, or nil
type Node: {
data ∈ Z,
next ∈ @Node?
} < Object;
nil is a singleton sentinel denoting the absence of a value. Every nil write aliases the same sentinel, so x = nil is decided by identity.
Reading an optional reference uses safe navigation ?., which short-circuits to nil rather than raising an error when the base (or any intermediate link) is nil (see spec/05 §1.3 and spec/06 §2.5):
new leaf := {data: 9, next: nil};
new head := {data: 1, next: leaf};
expect head.next?.data = 9; -- leaf is not nil, so .data reads
expect head.next?.next = nil; -- leaf.next is nil
The Void Type & the void object
Where nil is the singleton sentinel for the absence of a value, Void is a type: the type of a value that has no members. The canonical void object is an empty object void = {}.
new v := {}; -- empty object literal -> Void
new l := (); -- empty list literal -> Void
new a := []; -- empty array literal -> Void
expect v is Void; -- type-membership introspection
nil is not a type. A nil is the sentinel carried by an optional member (T?) that has been left unset; comparing to it (x = nil) is an identity check, not a type test.
Void is a type. An empty {}, (), or [] reports the type Void (spec/05 §1.4, spec/06 §1). Once a member is added to a void value it becomes an ordinary Object (attribute overlay, spec/06 §2.3).
Range Type
A range denotes a sub-set of consecutive numeric (or character) values between two limits. Endpoint inclusion is expressed by the range operator (Decision 13). Canonical operators are .. (both inclusive), ..< (exclude upper), >.. (exclude lower), and >..< (exclude both). A postfix step (min..max)(step) produces a stepped domain.
Syntax:
range ::= (min..max); -- both limits inclusive [min, max] range ::= (min..<max); -- exclude upper limit [min, max) range ::= (min>..max); -- exclude lower limit (min, max] range ::= (min>..<max); -- exclude both limits (min, max) range ::= (min..max)(step); -- stepped domain, 1-based indexing (Decision 1)
Notes:
- Both limits are included by default (..)
- Upper / lower / both limits are excluded with ..< / >.. / >..<
- Range notation can be open at one end or both ends
- Open lower limit is symbolic (-) with no number
- Open upper limit is symbolic (+) with no number
- Legacy forms .!, !., !! and the colon-step (min..max : step) are deprecated (Decision 13)
Example:
-- integer domain
rule main:
print (0..5); -- 0,1,2,3,4,5
print (0..<5); -- 0,1,2,3,4
print (0>..5); -- 1,2,3,4,5
print (0>..<5); -- 1,2,3,4
pass if 32667 ∈ (0..+); -- expect to pass
pass if -32668 ∈ (-..0); -- expect to pass
return;
Ranges can use symbols that are ASCII or Unicode. In this case the symbols must be included in single quotes: 'X' or use U+ notation:
Example:
-- sub-type declarations
type .Digit: ('0'..'9') <: Z;
type .Capital: ('A'..'Z') <: A;
type .Lowercase:('a'..'z') <: A;
type .Latin: (U+0041..U+FB02) <: U;
rule main:
-- following statements verify the domains
assert '0' ∈ Digit; -- '0' is a digit
assert not ('x' ∈ Capital); -- 'x' is not a capital letter
assert 'X' ∈ Capital; -- 'X' is a capital letter
assert 'e' ∈ Latin; -- 'e' is Latin
return;
Domain Type
Domain is very similar to range except a domain has a ratio — the numbers are not integers, they can be fractional or Q numbers. The step ratio is appended as a postfix extension (step) to the range (canonical form per Decision 13).
type Domain: (min..max)(step) <: Super_Type;
Examples:
-- generate rational numbers
print (0..1)(1\4); -- 0\4, 1\4, 2\4, 3\4, 1\1
-- generate float numbers (float step 0.25)
print (0..1)(0.25); -- 0.00, 0.25, 0.50, 0.75, 1.00
Constant declarations
Constants literals bound to identifiers using keyword "set".
-- using explicit type
set constant_name: constant_literal ∈ type_name;
-- using implicit type
set constant_name := constant_literal;
Notes:
- Unfortunate English is using "set" to define a sequence,
- In Bee we use "set" keyword to setup a constant not a sequence,
- Constant initial value can be assigned using operator ":=" or "=",
- Constants can be public, private or local,
- Public constants are defined using prefix ".",
- Local constants are defined in a local scope.
Example:
-- define symbol constants
set forall1: U+2200 ∈ A; -- Symbol: ∀
set forall2: U-00002200 ∈ U; -- Symbol: ∀
Note:
- After U+ compiler is expecting 4 hexadecimal symbols;
- After U- compiler is expecting 8 hexadecimal symbols;
Variable declarations
Variables are defined using type inference and these operators:
| operator | purpose |
|---|---|
| ∈ | declare variable/element type |
| : | define | block start | pair up operator |
| : | set initial value (require type in declarations) |
| := | type inference | share a reference | assign operator |
| :: | deep copy | duplicate object | cloning operator |
Notes:
- Symbol ":" can be used to define a type or set initial value or type for a variable or parameter.
- Symbol ":" can be used as pair-up operator for arguments and map pairs.
- Symbol ":=" can can be used to define or mutate the value of variables or parameters.
- Symbol "<:" used in declarations is used to indicate a supertype,
-- primitive variable declarations with type
new var_name ∈ type_name; -- declaration only type without initial value
new var_name: value ∈ type_name; -- declaration with initial value and type
-- Variable declaration using type inference
new var_name := expression; -- expression ":=" do not require type hint ("∈").
-- Multiple variables can be define in one single line using comma separator:
new var1, var2 ... ∈ TypeName; -- default initial values
new var1, var2 ... := Expression; -- use type inference for all initial values
-- Initialize multiple variables, of the same type (type is required)
new var1:con1, var2:con2 ... ∈ TypeName;
Alphanumeric type
Bee define A as single UTF-8 code point with representation: U+HH
rule main:
new a, b ∈ A; -- ASCII
new x, y ∈ B; -- Binary integer
let a :='0'; -- ASCII symbol '0'
let x := a :> B; -- convert to binary 30
let y := 30; -- decimal code for '0'
let b := y :> A; -- convert to ASCII symbol '0'
return;
Data Processing
Having strong type definitions, assume a specific advantages and special data processing considerations. This can be subject of more study and analysis later, let's define most important aspects of data processing.
Modify values
One can modify variables using alter statement.
Example:
new a:10, b:0 ∈ Z; -- initialize two variables
let b := a + 1; -- modify b using binding operator :=
let b += 1; -- modify b using modifier +=
expect b = 12; -- check if b has proper value
Notes:
- Multiple variables can be modified all at once when separated by comma;
- Following operators are called modifiers and can change value of a variable:
- {":=","::"}
- {"+=","-=","&\=","*=","^=","%=", "*=", "/=", "√="}
Examples:
-- declare a public constant
set .PI: 3.14 ∈ R;
rule main:
-- declare a single variable
new a ∈ Z; -- Integer
-- declare multiple variables
new (x,y):0 ∈ R; -- Double
new (q,p):0 ∈ B; -- Boolean
-- using modifiers
let a := 10; -- modify value of: a = 10
let a += 1; -- increment value of: a = 11
let a -= 1; -- decrement value of: a = 10
-- modify two variables using one single constant
let x, y := 10.5;
-- modify two variables using two constants
let q, p := True, False;
-- swapping two variables
let p, q := q, p;
return;
Type checking
We can use variable type to validate expression type.
-- using type inference
rule main:
new a := 0; -- integer variable
new b := 0.0; -- real variable
let a:= 10.5; -- error: explicit conversion is required
return;
You can use operator "∈" to verify data type:
rule main:
new a := 0 ∈ Z;
-- expected: Integer
expect a ∈ Z;
return;
Type conversion
When data type mismatch you must perform explicit conversion.
- Explicit conversion is using a symbolic operator: ":>"
- This is unsafe operation. A range check is recommended before conversion;
- Data precision may suffer. Some decimals may be lost;
- If data do not fit in the new type, the overflow exception is raised.
Example:
-- data conversion
rule main:
new a:0, b:20 ∈ Z;
new v:10.5, x:0.0 ∈ R;
-- explicit conversion
let a:=v :> N;
print a; -- truncated to 10
-- explicit conversion
let x := b :> R;
print x; -- expect 20.00
return;
Notes:
- making conversion will copy value not reference
- conversion to same data type is equivalent to copy "::"
Type promotion hierarchy
Bee defines a canonical implicit promotion chain (spec/05 §4.3/§5). A value may widen toward a larger mathematical space without an explicit cast; narrowing back (e.g. Float to Rational) always requires the explicit :> operator.
B → N → Z → R → Q → C
with two widening branches off that chain: Alpha (A) is promoted from Natural (N), and Unicode (U) from Integer (Z):
| Promoted from | to | Widening rule |
|---|---|---|
| B Boolean | N Natural | implicit |
| N Natural | A Alpha | implicit |
| N Natural | Z Integer | implicit |
| Z Integer | U Unicode | implicit |
| Z Integer | R Real | implicit |
| R Real | Q Rational | explicit :> only |
| Q Rational | C Complex | implicit |
Rational numbers convert to Real implicitly; Real to Rational requires an explicit cast :> Q (spec §4.3).
Boolean type
Boolean type is Bee is native numeric of type B. However we also implement native two constants True, False that are publicly available in core library
-- overwrite the default values
set False = 0;
set True = 1;
Printing native Boolean values is going to print numbers. Type B can support a number between 0b0 and 0b00000001. Therefore printing this type is done using notation 0b0 and 0b1.
-- printing Boolean values
rule main:
print False; -- 0b0
print True; -- 0b1
return;
You can convert B to a number, safely. You can convert a number to B explicit using cast operator: number :> B. The number will loose all it's significance and become 0b1 or 0b0.
Logic operations
Bee uses several familiar logic operators from mathematics:
- ! - equivalent "not"
- ∧ - equivalent "and"
- ∨ - equivalent "or"
Precedence: { !, ∧, ∨ }. Symbol ! will apply first, symbol ∨ will apply last.
bitwise
In Bee we define special operators to perform bitwise operations. One operator is overwrite: ⊕. This operator (xor) can operate on both, logical values or expressions and also on integer numbers.
- ~ (not)
- & (and)
- | (or)
- ⊕ (xor)
-- bitwise operations
print 4 | 3; -- out:7 is because 100 | 011 = 111 = 7;
print ~4; -- out:3 is because: 100 ~ 100 = 011 = 3;
print 4 & 7; -- out:4 is because: 100 & 111 = 100 = 4;
print 4 ⊕ 4; -- out:0 is because: 100 ⊕ 100 = 000 = 0;
print 1 << 2; -- out:2 is because: 001 << 2 = 100 = 4;
print 6 >> 2; -- out:1 is because: 110 >> 2 = 001 = 1;
Other operators
Comparison operators will create a Boolean response: 1 = True or 0 = False. Also, the equality and belonging operators will create a Boolean response. You can combine them to create Boolean expressions.
- comparison ( ≈, =, ¬, >, <, ≤, ≥)
- equality (=, ¬)
- belonging ( ∈, !∈ )
Example:
rule main:
new (x, y):4 ∈ Z; -- primitive integer
-- value comparison
print x = 4; -- 1 (equal)
print x = y; -- 1 (equal)
print x ¬ 5; -- 1 (different)
-- reference ordering
print x ≥ y; -- 1: x and a are actually equal
print x ≥ 4; -- 1: greater or equivalent to 4
print x ≤ 4; -- 1: less than or equivalent to 4
print x > 4; -- 0: not greater than 4
print x < 4; -- 0: not less than 4
-- arithmetic expressions have primitive results
print x - 4 = 0; -- 1
return;
singleton
Primitive types are unique. That means they are equal. A literal or constant is equal to another literal having the same value.
-- primitive values are singleton
print 1 = 1; -- 1
print "s" = "s"; -- 1
Precedence:
Logic operators have greater precedence than comparison operators.
Logical expression
Logical expression have value { 0 = False, 1 = True }
-- define constants based on Boolean values
set f: False;
set t: True;
rule main:
-- expressions with single operant
print f; -- 0
print ! t; -- 0
-- expressions with two operands
print (f = t); -- 0
print (f ¬ t); -- 1
print (f < t); -- 1
print (f > t); -- 0
print (f ∧ t); -- 0
print (f ∨ t); -- 1
print (f ⊕ t); -- 1
return;
Notes:
- Operators { ! } is unary operator;
- Operators { ∧, ∨ } are also bitwise operators;
- Operators { !, ⊕ } are also bitwise operators;
- Operators { «, » } are bitwise operators;
coercion Any numeric expression can be converted to a logic value using coercion operator ":>" (easy to memorize if you think is like an arrow).
set (a: 0.0, b: 1.5) ∈ R;
rule main:
new (x, y) ∈ B;
let x := a :> B; -- 0
let y := b :> B; -- 1
return;
Notes:
- Real number is truncated before conversion to Boolean;
- A string: "Yes", "yes", "True", "true", "On","on", "T", "t" or "1" convert to: True = 1
- A string: "No", "no", "False", "false", "Off","off", "F", "f" or "0" convert to: False = 0
design
-- logical values are numeric
print True = 1; -- 1
print False = 0; -- 1
-- logical values do not compute
print False - True; -- Error
print True + True; -- Error
-- Null value is not True
print Null = True -- 0
-- Null value is not False
print Null = False -- 0
Type inference
Bee is strongly typed but uses type inference for a softer learning curve and code simplification. Type inference is a logical deduction of data type from constant literals. You can use symbol ":=" to initialize variables using type inference like in the following example:
-- declare constants
set i := 4; -- integer constant
set r := 2.5; -- real constant
set q := 1&\8; -- rational constant
-- declare variables
new x := 0; -- integer number
new y := 0.0; -- real number
new z := 0\1; -- rational number
Default types
Each literal has associated a default type, induced by operators {":=", "::"}.
-- string expressions
new c := 'a' ; -- type = A
new s := '∈' ; -- type = U
new s := "str" ; -- type = S
-- numeric expressions
new i := 0; -- type = Z
new j := 0.50; -- type = R
-- define synonyms for logic constants
set f := False; -- type B
set t := True; -- type B
-- multiple variables get same value
new (x, y, z) := 5; -- type = Z for all
-- multiple variables get multiple values
new int, rea := -4, 4.44;
print type(int); -- Z
print type(rea); -- R
Composite Types
Composite structures are using () [] and {} to create different data types. Next you can study some examples. We wil explain later all these types: List, Map, Array, Object. All of these are also called Bee collections.
-- boxed integer (type = Z)
new i := [10]; -- array of one value = 10!
-- array with capacity of 4 integers: Z
new d := [1,2,3,4];
-- array with capacity of 10 real numbers: R = 0.00
new e := [0.00](10);
-- list of one value (Z)
new a := (1);
-- list of integers (Z)
new b := (1,2);
-- list of symbols (type = A)
new l := ('a','b');
-- list of Unicode symbol (type = U)
new u := ("Δ", "Λ", "Γ");
-- 2d matrix with capacity of 10x10 real numbers: R
new m := [0.00](10,10);
-- 3d matrix with capacity of 10x10x10 reals: R = 0.00
new m := [0.00](10,10,10);
-- data set of 4 integers: Z
new s := {1,2,3,4};
-- hash map of (Integer: String)
new c := {1:"storage",2:"string"};
-- object with two attributes: name ∈ S, age ∈ Z
new b := {name:"Goliath", age:30};
Parameter types
When we define parameters we can use type inference only for optional parameters. Mandatory parameters must have type declaration using ∈ symbol. If default value is not specified the parameter will have default zero initial value.
Optional Parameters:
-- in rule foo, parameters a, b are optional.
rule foo(a: 0 ∈ Z, b: 0 ∈ Z) => (r ∈ Z):
let r := a + b;
rule;
rule main:
print foo(); -- 0
print foo(1); -- 1
print foo(1,2); -- 3
return;
Multiple parameters:
-- parameters: a, b are mandatory, c is optional.
rule foo(a, b, c:0 ∈ Z) => (r ∈ Z):
let r := a + b + c;
return;
rule main:
print foo(1,2); -- 3
print foo(1,2,3); -- 6
print foo(1); -- Error: expected 2 arguments
return;
Pass arguments by name:
We can use parameter name and pair-up symbol ":" for argument value.
-- rule with optional parameter (c)
rule bar(a, b, c:0 ∈ Z) => (result ∈ Z):
let result := a+b+c;
return;
-- observe we use pair-up ":" to give value for each argument
rule main:
print bar( 1, 1 ); -- print 2 because c = 0
print bar( a:1, b:1, c:1 ); -- print 3 because c = 1
return;
Rational numbers
In mathematics rational number is any number that can be expressed as the fraction p/q of two integer numbers: numerator "p" of type integer and a non-zero denominator "q" of type natural> 0.
Since "q" may be equal to 1, every binary integer is also a rational number.
Note: Q numbers are approximated numbers.
- Default precision for Q numbers is $max_precision: 10⁻⁵ = 0.00001
- Default precision can be set using: $max_precision:x; global constant
- Operator "≈" is using the precision if the operator "±" is not used;
Other precision constants:
- $dec := 1d = 10⁻¹
- $cen := 1c = 10⁻²
- $mil := 1m = 10⁻³
- $mic := 1μ = 10⁻⁶
Literal Notation: p\q
It can be used with type inference to create Q numbers:
Example:
new x: 0 ∈ Q; -- 0
new a: 1\2 ∈ Q; -- 0.5
new b: 1\4 ∈ Q; -- 0.25
new c: 1\8 ∈ Q; -- 0.125
new d: 1\16 ∈ Q; -- 0.062
new e: 1\32 ∈ Q; -- 0.031
Note:
- The inch is a unit of length in the British imperial and United States
- It is equal to 1\36 yard or 1\12 of a foot
- One inch is divided in 1\2, 1\4, 1\8, 1\16 and 1\32
See also: wikipedia
Q Notation
-
Qm.n is m+n+1 bit signed integer container with n fractional bits.
-
Qm is a m+1 bit signed integer containing 0 fractional bits.
-
number of bits = m+n+1
-
precision is 2⁻ⁿ
-
range is [-(2ᵐ)..(2ᵐ-2⁻ⁿ)]
For example: Number format "Q5.2" can store in range (-32.00..31.75) on 8 bits.
- with precision of 2⁻² = 1\4 = 0.25
- from value: -2⁵ = -32.00
- to value: 2⁵ - 2⁻² = 32 - 0.25 = 31.75
new v ∈ Q5.2;
let v := -32; -- minim value
let v := 31.75; -- maxim value
See also: wikipedia
Typical Q numbers
Next I have predefined some numbers for orientation.
| resolution -> | 1\4 ≈ | 1\8 ≈ | 1\16 ≈ | 1\32 ≈ | 1\64 ≈ |
|---|---|---|---|---|---|
| ↓ memory space | ±0.25 | ±0.125 | ±0.062 | ±0.031 | ±0.015 |
| 8 bytes | Q(5.2 ) | Q(4.3 ) | Q(3.4 ) | Q(2.5 ) | Q(1.6 ) |
| 16 bytes | Q(13.2 ) | Q(12.3 ) | Q(11.4 ) | Q(10.5 ) | Q(9.6 ) |
| 32 bytes | Q(29.2 ) | Q(28.3 ) | Q(27.4 ) | Q(26.5 ) | Q(25.6 ) |
| 64 bytes | Q(61.2 ) | Q(60.3 ) | Q(59.4 ) | Q(58.5 ) | Q(56.6 ) |
| 128 bytes | Q(125.2) | Q(124.3) | Q(123.4) | Q(122.5) | Q(121.6) |
Note: r ≈ is the approximate resolution.
Examples:
A very large number with high resolution on 64 bit:
Q(50.12)
- Min: -1125899906842624
- Max: +1125899906842623
- Res: 0.000244140625
A number on 32 bit with resolution = 0.0005:
Q(20.11)
- Min: -1048576
- Max: 1048575
- Res: 0.00048828125
Default Q number
Q(14.17)
Bee is using default Q type for type inference, when precision is not specified. Default Q number has precision 10⁻⁵ = 2⁻¹⁷ ≈ 0.00001 and occupy 32 bit. It's a relative small number. For larger numbers you must specify the resolution and capacity.
Range:
- Min: -16384
- Max: +16383
- Res: 0.0000076
Fraction:
- Smallest fraction: 1\10000
- Largest fraction: 32766\2
Approximate comparison
Rational numbers and other numbers can be compared using "≈" instead of "=". Bee can be used to make approximate computations and logic expressions based on precision.
- Operator "≈" can be used to compare two numbers using default precision;
- Operator "≈" can be used with "±" to overwrite default precision;
Example:
In next example b = 0.33(3), delta = (b - a) = 0.083
-- override default precision
set $max_precision := 0.01;
rule main:
new a := 0.25; -- real
new b := 1\3; -- rational O.33(3)
-- using specified precision 0.01
print (a ≈ b); -- 0B0: (False, 0.033 - 0.25 = 0.08 )
-- using specified precision:
print (a ≈ b ± 0.01); -- 0b0 (False)
print (a ≈ b ± 0.05); -- 0b0 (False)
print (a ≈ b ± 0.10); -- 0b1 (True)
return
Notes:
- Operator "\" has high priority in expressions;
- Rational numbers improve computation efficiency;
Conversion
Rational numbers are similar to complex numbers. The calculation of rational number is postponed until is necessary in expressions and it can produce different results depending on the precision.
- Conversion from rational number to real is implicit;
- Conversion from real to rational is explicit;
-- execute conversion
rule main:
new a := 0.25; -- real
new b := 1\4; -- rational
-- explicit conversion R :> Q
expect a ¬ b; -- not equal, type mismatch
expect a ≈ b; -- values match, approximation
-- explicit conversion R :> Q
new c := a :> Q;
expect c = b; -- type & value match
-- implicit conversion Q :> R
new r := c ∈ R;
expect c = 0.25;
return
Diagnostic error codes
Diagnostic codes raised by the Types module (spec/05 §7):
| Code | Violation | Mitigation / diagnostic action |
|---|---|---|
| E0501 | TypeMismatch: Assigned value violates the static type boundary. | Cast or fix the value to the declared type. |
| E0502 | InvalidTypeCast: Incompatible explicit cast with the :> operator. | Use a lossless cast between compatible types. |
| E0503 | DomainOutOfBounds: Assigned literal falls outside a constrained range/domain. | Constrain the value to the declared domain. |
| E0504 | RationalOverflow: Value exceeds the maximum capacity of a Q(m.n) container. | Widen the fixed-point precision. |
| E0505 | SingleLetterTypeReserved: User type identifier uses a reserved single uppercase Latin letter. | Choose a non-reserved name or a multi-letter type. |
| E0506 | PrecisionUndefined: Approximate comparison ≈ executed with uninitialized $max_precision. | Initialize $max_precision before using ≈. |