-
Notifications
You must be signed in to change notification settings - Fork 0
Language Syntax
You can redefine variables.
You cannot mutate their values.
For example:
let x = {
name: "Person 1",
}
x = { name: "Person 2", }
// is valid
x.name = "Person 2"
// is not valid
fn init() =>
{ # a record
message: "This is a test", # a string
success: false, # a boolean
count: 10, # a number
items: [ # a seq
1,
10,
100,
]
}
Kura supports the following primitives:
- Bools
- Numbers
- Strings
- Records
- Seq, or Sequences
- Functions
- Algebraic Types
- Optional Types
let data = [
1px,
1px,
1px,
]
Sequences represent series - lists or arrays, of data.
The compiler will attempt to infer and optimize the layout of the Sequences internally. The compiler will not guarantee the layout of a Sequence.
You should not rely on it.
Always treat Sequences as just a sequence - or list, of data rather than a contiguous array of data.
// you can combine Sequences together using the + operator
let items1 = [ item1, item2 ]
let items2 = [ item3, item4 ]
let items = items1 + items2
// this creates a new Sequence named items
// Equivalent to: [ item1, item2, item3, item4 ]
Records hold key-value pairs and become immutable. Like Python Dictionaries or Javascript / JSON Objects.
let data = {
status: 10,
success: false,
message: "Hello World",
}
// the following is an invalid record definition:
data = {
10,
}
// or this:
data = {
get-data(),
}
// or this:
data = {
get-data(),
}
// or this:
data = {
property: value,
get-data(),
}
// or this:
data = {
property: value,
10,
}
You cannot change the properties of a Record once you construct a Record instance. You can only create a new Record instance.
let data = {
success: false
}
// the following is not allowed:
data.success = false
// neither is this
data.message = "Hello World"
// however, this is acceptable
data = { success: true }
// or this
data = {
...data,
message: "Hello World",
}
You can also use "Record Comprehension" - just like in Python and other data heavy languages, to compose Records.
let props1 = {
fill: blue,
}
// and:
let props2 = {
...props1,
insets: [ 1px, 1px, 1px, 1px ],
} // becomes:
// {
// fill: blue,
// insets: [ 1px, 1px, 1px, 1px ],
// }
// and:
let props3 = {
...props1,
...props2,
isDebug: true,
} // becomes:
// {
// fill: blue,
// insets: [ 1px, 1px, 1px, 1px ],
// isDebug: true,
// }
You can also combine them together using the + operator
let r1 = { status: 10, }
let r2 = {
success: true,
message: "Finished",
}
let r3 = r1 + r2 // becomes
// {
// status: 10,
// success: true,
// message: "Finished",
// }
And
let r4 = r3 + {
isDebug: true,
} // becomes
// {
// status: 10,
// success: true,
// message: "Finished",
// isDebug: true,
// }
And
let r5 = {
...(r1 + r2),
component: "Button",
} // becomes
// {
// status: 10,
// success: true,
// message: "Finished",
// component: "Button",
// }
fn add(x, y) =>
x + y
Kura supports first-class functions.
fn apply(op, x, y) => op(x, y)
// Since no type is specified
// the compiler will infer the type of 'x'
let x = 10
Kura's compiler will use type-inference to determine types if no type specification exists. If it can't determine the type, or if an invalid type specification occurs, the compiler will throw an error.
// The type of "Sequence" is valid for the value of 'x'
// no compilation errors are thrown
let x: Sequence = [ "Hello World" ]
// The type of "Number" is not valid for the value of 'x'
// a compilation error will be thrown here
let y: Number = "Hello World"
You can use the following for Kura's primitive types:
| Type | Type Specification | Example |
|---|---|---|
| Strings | String |
type StringAlias = String or let x: String = "Hello World"
|
| Numbers | Number |
type NumberAlias = Number or let x: Number = 10
|
| Bools | Bool |
type BoolAlias = Bool or let x: Bool = false
|
| Records | Record |
type RecordAlias = Record or let x: Record = {}
|
| Sequences | Seq |
type SequenceAlias = Sequence or let x: Sequence = []
|
| Functions | Function |
type FunctionAlias = Function or let x = (Number, Number) => Number
|
You can also specify a "Typed Record" like so:
type Position = {
x: Number,
y: Number,
}
// this returns a Record that matches the type of Position
fn get-pos => {
x: 10,
y: 20,
}
Record types can also declare optional properties like so:
type Position = {
x: Number,
y: Number,
z: Optional<Number>,
}
// the return of this function is a record that matches
// the type of Position
fn get-pos-2d => {
x: 10,
y: 10,
}
// same here, the return of this function
// is a record that matches the type of Position
fn get-pos-3d => {
x: 10,
y: 10,
z: 10,
}
For some shorthand syntactical sugar you can express optional properties like so:
type Position = {
x: Number,
y: Number,
z?: Number, // this just means Optional<Number> to the compiler.
}
Kura supports algebraic types.
This allows for type unions.
// type union
type Message = Click | Tick
You can also have generic types in Kura
type Optional<T> = Some(T) | None
// and
type Result<T, E> = Ok(T) | Error(E)
Directives influence what the compiler should do during compilation.
Directives hint to the compiler on what it should do.
Directives do not guarantee that the compiler will do something specific.
Example:
// the following will hint to the compiler that the
// sequence value can be treated like a vectorized array
// this can improve maths operations significantly
@simd
let pos = [ 10, 10, 10, ]
// by default, the compiler will attempt to do this automatically
// however by adding the directive you can influence its decision
// to apply related optimizations to the value
// this still does not mean it will do this by default
// nor does it mean that pos is a contiguous value.
// *do not rely on the directives*
// the value for pos may not be contiguous
// *these are only hints* to the compiler
You can read more about Directives including the currently supported ones in Directives.