Skip to content
This repository has been archived by the owner on Nov 16, 2023. It is now read-only.

Latest commit

 

History

History
310 lines (229 loc) · 17.7 KB

overview.md

File metadata and controls

310 lines (229 loc) · 17.7 KB

Bosque Language Overview

The Bosque language is a hybrid of functional programming language semantics and an ergonomic block & assignment-based syntax. This allows developers to organize code into familiar/natural blocks and compositions while, simultaneously, benefiting from the correctness and simplicity of a functional programming model.

Table of Contents

Type System

  1. Primitive Types
  2. Nominal Types
  3. Structural Types
  4. Code Block Types
  5. Combination Types
  6. Ephemeral Lists
  7. Containers

Type Checking and Inference

Declarations

  1. Namespaces
  2. Type Members

Expressions

  1. Dispatch & Invoke

  2. Literals

  3. Variables

  4. Scoped Access

  5. Logical Operators

  6. Numeric Comparison

  7. Numeric Operators

  8. K-Comparison

  9. Static Operator Invoke

  10. Dynamic Operator Invoke

  1. Function Invoke
  2. Method Invoke
  3. Operator Invoke

Statements

KeyType

APIType & TestableType

Type System

The Bosque language supports a simple and non-opinionated type system that allows developers to use a range of structural, nominal, and combination types to best convey their intent and flexibly encode the relevant features of the problem domain. All type names must start with a capital letter - MyType is a valid type name while myType is not. Template type names are single capital letters -- T, V, etc.

Primitive Types

Bosque provides a range of standard primitive types, numerics, strings, times, etc. as part of the core implementation.

  • None: The type None is the special primitive none-able type that has the single (unique) none value.
  • Nothing: The type Nothing special primitive nothing Option<T> type that has the single (unique) nothing value.
  • Bool: The type Bool is contains the special true and false values.
  • Nat & Int: The Nat and Int types represent unsigned and signed (respectively) 64 bit numbers. Overflows, underflows, and div-by-0 all raise fatal errors.
  • BigNat & BigInt: The BigNat and BigInt types represent unsigned and signed (respectively) unbounded integral numbers. Underflows and div-by-0 raise fatal errors while overflows are either limited to an implementation defined max (at least 256 bits or Out-of-Memory) and saturating operations ++, --, ** are provided (TODO).
  • Float & Decimal: The Float type is a 64 bit IEEE-754 floating point number. The Decimal type is a 64 bit decimal floating point number.
  • Rational: The Rational type is a rational representation of a number with a BigInt valued numerator and a Nat valued (non-zero) denominator. Overflow in the denominator is handled by rounding to the nearest representable value.
  • String: The String type in Bosque is a utf-8 unicode string. Notably this string type does not support arbitrary indexing (which is undefined for utf-8 multibyte characters). Instead operations must use regex based slicing, extraction, etc.
  • ASCIIString: (TODO) The ASCIIString type is a ASCII char based string that can be meaningfully processed using integral index operations.
  • ByteBuffer: The ByteBuffer type is a 0 indexed array of uninterpreted 8 bit values.
  • Regex: the Regex type is the type assigned to all regex literals in the program.

In addition to the basic types enumerated above, Bosque also provides a range of commonly useful types for dealing with time, locations, events, and identity.

  • DateTime: The DateTime type represents a human scale time with minute precision (so no leap second issues). The representation is TimeZone based and does not allow naive comparision for ordering or computation of offsets as these are ill defined and non-deterministic operations (e.g. when the times are in the future a TZ meaning may change).
  • UTCDateTime: The UTCDateTime type is a specialized version of DateTime that is fixed at UTC for the timezone. This allows us to do direct ordering and arithmetic on the dates.
  • CalendarDate: The CalendarDate type is a date only value, month, day, year, so it is free of TZ related complications and can be directly ordered and used for offset date computation.
  • TickTime: the TickTime type is a 54 bit, nano-second interval, epoch based time TAI derived. This time is monotone and corresponds to real elapsed time.
  • LogicalTime: the LogicalTime type is a logical tick based time useful for causal ordering events in a system.
  • ISOTimeStamp: the ISOTimeStamp type is a ISO 8061 timestamp with milliseconds and a timezone. It is intended to be used for legacy interop and for human friendly timestamps in logs or other records. Historical values are stable but future values may be unstable as time-zone changes or leap-seconds are added.
  • UUID4 & UUID7: the UUID types for UUID4 and UUID7 are natively supported in Bosque.
  • SHAContentHash: the SHAContentHash type is a SHA3 512 bit hash code. Bosque has support (TODO) for computing SHA hashes of any value and can produce a result of type SHAContentHash or SHAContentHashOf<T>.
  • LatLongCoordinate:: the LatLongCoordinate is a decimal degree encoding (6 decimal digits) of a latitude and longitude value.

Nominal Types

The nominal type system is a mostly standard object-oriented design with parametric polymorphism provided by generics. Bosque also supports type aliasing typedef and wrapping of primitive types with typedecl.

Entity

Entities are concrete object-oriented style (member fields, methods, etc.) types that can be created. An entity will never have another type that is a strict subtype of it! Entities are always subtypes of the special Some and Any concepts and user defined entities are always subtypes of the special Object concept. Entities can be declared as polymorphic with template parameters. In Bosque templates are not parametric in their instantiated types -- e.g. List<Int> is not a subtype of List<Any>.

Entity type references are simply the (scoped) name of the type such as Foo for a locally scoped type or Bar::Foo, Bar::Baz::Foo for a type Baz declared in the given scope (namespace or type scope). Entity types may also be parametric, such as List<Int> or Foo<Bool, Map<Int, Int>>.

In Bosque the typedef declaration can be used to create a nominal alias for any type. This alias is not a distinct type and, in type checking and execution, is replaced by the underlying aliased type (see also typedecl below).

There are several special entity types in Bosque:

  • Enum: Bosque supports enum types as nominal types. The underlying values can be any typedeclable type but the enum type is always a distinct type in the program.
  • typedecl: To create a new and distnct nominal type for any typedeclable type the typedecl keyword can be used to create a new type that is has an identical value representation (accessable via the value method) to the underlying type but is a distinct type in the program.
  • StringOf<T> & DataString<T&gt: The StringOf<T> and DataString<T> types in the program (also ASCIIStringOf<T> and ASCIIDataString<T> types) are dsistinct string types. The StringOf flavors are parameterized by Validator regexes (the underlying string is in the specified language) while the DataString flavors are parameterized by types that provide the Parsable concept (i.e. they have an accepts function).
  • DataBuffer<T>: The DataBuffer<T> type is similar to the DataString<T> type except the underlying value is a ByteBuffer.
  • Something<T>: is the subtype of Option<T> that represents a value (as opposed to the special Nothing subtype). There is a special constructor operator something that can be used as a type infering constructor in addition to explicit construction.
  • Result<T, E>::Ok & Result<T, E>::Err: Bosque provides a standard Result<T, E> concept with Ok and Err entities. If omitted from the type the error E type is assumed to be None. As with Something<T> there are special (shorthand) type inferring keywords ok and err for constructing these types.

Bosque also provides a range of built-in container types List<T>, Stack<T>, Queue<T>, Set<T>, and Map<K, V> which are described in more detail in the libraries document.

Concept

Bosque concept types are fully abstract and can never be instantiated concretely. Concepts can be provided by concrete entities in the program. Concept types can define virtual or concrete methods, fields, and constants. Concepts are referenced by names (just like entities) such as Some, Result<Int>, etc.

There are several special concept types in Bosque:

  • Any: The Any type is an ur concept that every type in Bosque is a subtype of.
  • Some: The Some type is a concept type that every non-none type is a subtype of.
  • KeyType: The KeyType concept is a critical type in Bosque. The === and !== operator are defined to work only on such types and keys in Set<T> and Map<K, V> must be subtypes of KeyType. The list of keytypes and mor information on their behavior is included in the KeyType section.
  • APIType: The special APIType concept is used to identify which types can/are exposed in entrypoint functions. These types are ensured to be 1) convertible to work in polyglot environments and 2) are amenable to structured generation for fuzzing and testing. See the APIType & TestableType section for more detail.
  • TestableType: The special TestableType concept is used to identify which types can/are exposed in unit-test functions. This is similar to the APIType concept but used for types that are not intended to be publicly exposed (just visible for testing purposes). See the APIType & TestableType section for more detail.
  • Option: Bosque supports a familiar Option<T> concept for convenient use of sentinel missing values (nothing).
  • Result<T, E>: Bosque supports a standard Result<T, E> type for convenient result value management. When not explicitly provided the error (E) type defaults to None.
  • Object: All user defined entities implicitly provide the Object concept.

Concept types can be stacked, via the & combinator, to indicate a value/variable should provide multiple concepts. So, the type reference KeyType & APIType indicates that the type provide both the KeyType concept and the APIType concept.

KeyType

APIType & TestableType

Expressions

Dispatch & Invoke

XXXX

Literals

XXXX

Variables

XXXX

Scoped Access

XXXX

Logical Operators

Bosque supports a standard set of logical operations on Bool typed values:

  • ! is a negation of a Bool value.
  • && is a short circuiting logical and of two Bool values.
  • || is a short circuiting logical or of two Bool values.
  • ==> is a short circuiting logical implies of Bool values -- note that && and || bind tighter tha ==> when parsing logical expressions.
!true   //false
!false  //true
!0      //type error

true && false            //false
true && true             //true
false && (1n / 0n) == 1n //false -- short circuit is safe

true || false           //true
false || false          //false
true || (1n / 0n) == 1n //true -- short circuit is safe

true ==> false            //false
true ==> true             //true
false ==> true            //true (antecedent is false)
false ==> (1n / 0n) == 1n //true -- short circuit is safe

In some cases (i.e. to avoid branching or for clarity) it is desireable to use non-short-circuiting logical operations. Thus, Bosque also provides an:

  • /\(...) is a logical and of all arguments (semantically evaluated simultaneously).
  • \/(...) is a logical or of all arguments (semantically evaluated simultaneously).
/\(true, false, true)      //false
/\(true, true)             //true
/\(false, (1n / 0n) == 1n) //runtime error -- no short circuit

\/(true, false, true)     //true
\/(false, false)          //false
\/(true, (1n / 0n) == 1n) //runtime error -- no short circuit

Numeric-Comparison

Bosque supports a range of comparison operators, ==, !=,<, >, <=, and >= which can be applied to numeric values -- Nat, Int, BigNat, BigInt, Float, Decimal, and Rational values as well as orderable typedecl based types (e.g. orderable typedecl ItemCount = Int;) based on them.

0i == 1i                    //false
1i == 1i                    //true

0n < 5n                     //true
0n < 5i                     //type error not same types

10_ItemCount >= 0_ItemCount //true

The (numeric) comparison operators in Bosque are implemented as static operators and so can be extended by user defined types as well.

entity Complex {
    field r: Float;
    field i: Float;
}

infix operator ==(a: Complex, b: Complex): Bool {
    return a.r == b.r && a.i == b.i;
}

infix operator !=(a: Complex, b: Complex): Bool {
    return a.r != b.r || a.i != b.i;
}
...

Complex{1.0f, 0.0f} != Complex{1.0f, 1.0f} //true

Then calls where the LHS and RHS arguments resolve to Complex types will resolve to this implementation transparently.

Numeric Operators

Bosque supports a range of numeric arithmetic operators, +, -,*, /, <= which can be applied to numeric values -- Nat, Int, BigNat, BigInt, Float, Decimal, and Rational values as well as algebraic typedecl based types (e.g. algebraic typedecl ItemCount = Int;) based on them. The signed types also support unary negation -x.

0i + 1i                    //1i
1i - 1i                    //0i
3n * 5n                    //15n
3n / 2n                    //1n
3.0f / 2.0f                //1.5f

0n + 5i                    //type error not same types

10_ItemCount + 1_ItemCount //11_ItemCount
10_ItemCount * 2i          //20_ItemCount
10_ItemCount / 5_ItemCount //2i

In xxxx;

The (numeric) comparison operators in Bosque are implemented as static operators and so can be extended by user defined types as well (TODO: see issue for dynamic vs. static operator).

entity Complex {
    field r: Float;
    field i: Float;
}

infix operator +(a: Complex, b: Complex): Complex {
    return Complex{a.r + b.r, a.i + b.i};
}

infix operator *(a: Complex, b: Float): Complex {
    return Complex{a.r * b, a.i + b};
}

infix operator *(a: Complex, b: Complex): Complex {
    let rpart = a.r * b.r - a.i * b.i;
    let ipart = a.r * b.i + a.i * b.r;

    return Complex{rpart, ipart};
}
...

Complex{1.0f, 0.0f} + 2.0f                //Complex{2.0f, 0.0f}
Complex{1.0f, 0.0f} + Complex{0.0f, 1.0f} //Complex{1.0f, 1.0f}

Then calls where the LHS and RHS arguments resolve to Complex types will resolve to this implementation transparently.

K-Comparison