1# Ix Programming Language
2
3Ix (**I**nferable E**x**pression) is an experimental programming language.
4
5Key goals of this project are:
6
7- **Ease of Use**: Use of type inference and simple syntax makes code easy to
8 read and write.
9- **Fast Iteration**: Incremental compilation keeps development productivity
10 high.
11- **Safety**: Compiler errors catch most mistakes at compile-time, and
12 additional tooling to catch mistakes at runtime.
13- **Universal**: One codebase can seamlessly target different web browsers and
14 native targets.
15
16It primarily focuses on merging some great points of Rust, Zig, and JavaScript's
17language designs:
18
19- [**Rust**](https://www.rust-lang.org) - Expressive and concise control flow
20 that is easy to write.
21- [**Zig**](https://ziglang.org) - Execute any code at compile time; everything
22 is an expression.
23- [**JavaScript**](https://developer.mozilla.org/en-US/docs/Web/JavaScript) -
24 Garbage collected values with a versatile type system.
25
26This manual is a standalone HTML document. Code samples are compiled and tested
27as a part of the release cycle.
28
29## Hello World
30
31```lang:run
32let print = @extern("console").log;
33fn main {
34 print("Hello, world!");
35}
36```
37
38The `print` function is implemented by getting an external reference to the
39`console` JavaScript global, retrieving the `log` property from it, and then
40calling it when the program starts.
41
42> In the future, common APIs will be available in the standard library, such as
43> `std.log.info()`. For now, no such library exists.
44
45## Module Scope and Declarations
46
47The top-level
48
49```
50let x = 1;
51```
52
53## Primitive Data Types
54
55| name | ts equivalent | description |
56| ---------- | ----------------------------- | ---------------------------------------------- |
57| `bool` | `boolean` | either `true` or `false` |
58| `float` | `number` | a 64-bit floating point number |
59| `int` | `number` | an integer number in signed 32-bit range |
60| `bigint` | `bigint` | an integer number with no range limit |
61| `string` | `string` | a sequence of text characters |
62| `void` | `undefined` / `null` / `void` | detectable lack of a value |
63| `opaque` | `unknown` / `any` | could be any value |
64| `noreturn` | `never` | control flow prevents this value from existing |
65
66> **Note**: Unlike JavaScript, the values `undefined` and `null` are merged into
67> one concept: [void](#void)
68
69### Built-in Constants
70
71| name | description |
72| ------- | ------------------------------------- |
73| `true` | boolean true (result of `1 == 1`) |
74| `false` | boolean false (result of `1 == 2`) |
75| `std` | handle to the standard library module |
76
77These constants act as reserved names, and it is an error to re-declare them.
78Note that primitive data types are also considered built-in constants.
79
80```lang:build
81fn main {
82 let opaque = -5.2;
83 let true = 1 == 0;
84}
85```
86
87### Numbers
88
89Numbers have the following rules:
90
91- May start with a `.`, but cannot end with `.`
92- May contain leading zeros, but transform pass will warn.
93- May contain a `_` separator, cannot have two in a row.
94- May contain `e` or `E` for exponents.
95- May use 0x, 0b, 0o prefixes for hex, binary, octal numbers.
96
97### Strings
98
99Numbers
100
101### Multi-line string literals
102
103```lang
104let bwaa =
105 \\hello
106 \\ world
107 \\end
108;
109```
110
111### Void
112
113Void is the lack a value. For example, a function that does nothing returns
114`void`.
115
116```lang
117pub fn hello() {
118 // do nothing
119}
120```
121
122<!-- TODO: change this section to "Optionals" -->
123
124## Variables
125
126There are two types of variables, `let` and `var`.
127
128`let` bindings cannot be overridden, and should be thought of as aliases of the
129expression within. Referring to these identifiers is equivalent to inlining it
130where it was used, aside from side effect duplication.
131
132```lang:run
133let print = @extern("console").log;
134
135fn main {
136 let hello: int = 1;
137 let world = hello + 2;
138 let hello = hello * world;
139
140 print(hello);
141}
142```
143
144`var` bindings create mutable memory locations at runtime. This means they can
145be re-assigned to:
146
147```lang:run
148fn main {
149 var message = "first message";
150
151 if @extern("Math").random() > 0.5 {
152 message = "second message";
153 }
154
155 @extern("console").log(message);
156}
157```
158
159Using `let` is preferred whenever possible, as it will lead to more readable
160code, and the compiler is able to optimize it better. The above can be rewritten
161as:
162
163```lang:run
164fn main {
165 let message = if @extern("Math").random() > 0.5 {
166 "second message"
167 } else {
168 "first message"
169 };
170
171 @extern("console").log(message);
172}
173```
174
175It should be noted that numeric literals are a comptime-only data type, meaning
176it cannot be assigned to a `var` without an explicit type annotation. Since
177`int` is a strict subset of valid `float`s, the coercion will implicitly be a
178float, but a compiler warning will be emitted.
179
180```lang
181var number = 0; // `number` is `float`, but not explicit
182var number: int = 0; // `number` is `int`
183var number: float = 0; // `number` is `float`
184```
185
186## Functions
187
188Functions are declared with the `fn` keyword. Parameter types must be annotated.
189The function body is a block, which may use implicit return to return.
190
191```lang:run
192let print = @extern("console").log;
193
194fn add(a: int, b: int) {
195 a + b
196}
197
198fn main {
199 print("Sum of 2 and 7:", add(2, 7));
200}
201```
202
203The `return` expression can be used within functions to return early.
204
205```lang
206let print = @extern("console").log;
207
208fn something(a: int) -> bool {
209 if a == 0 {
210 return false;
211 }
212
213 a > 100
214}
215
216fn main {
217 print(something(0xFF), something(0));
218}
219```
220
221## Number Literals
222
223TODO
224
225## String Literals
226
227TODO
228
229## JavaScript Interop
230
231A global variable handle can be retrieved with `@extern`. We've seen this in the
232hello world. The type of this value is a special type called `opaque`, which
233disables type-checking.
234
235> **Note**: A future update will improve on the type-safety of `opaque`
236
237Many JS-exclusive actions are exposed via built-in functions, such as using
238`@jsNew` for calling constructors.
239
240```lang:run
241let print = @extern("console").log;
242
243fn currentYear() -> int {
244 @jsNew(@extern("Date")).getFullYear()
245}
246
247fn main {
248 print(currentYear());
249}
250```
251
252A field access on an external handle is allowed to be performed at compile time,
253where such access will only crash at runtime when the value is used.
254
255## Structs
256
257To group data together, a `struct` can be used. A struct contains zero or one
258fields of runtime-compatible types.
259
260```lang:run
261let print = @extern("console").log;
262
263struct Position {
264 x: int,
265 y: int,
266}
267
268fn main {
269 let center = Position{ x: 0, y: -2 };
270 print(center.x, center.y);
271
272 // To allow less verbose code, the struct type can be inferred with `.`
273 let added = addPositions(
274 .{ x: 1, y: -4 },
275 .{ x: 5, y: 8 },
276 );
277 print("Add result:", added.x, added.y);
278}
279
280fn addPositions(a: Position, b: Position) -> Position {
281 .{ x: a.x + b.x, y: a.y + b.y }
282}
283```
284
285To allow optimizations, `struct`s by default do not have a defined
286representation in JavaScript. This means passing the struct to an opaque
287function is not allowed.
288
289```lang:build
290let print = @extern("console").log;
291
292struct Person {
293 name: string,
294 age: int,
295}
296
297fn main {
298 print(Person{ name: "hello", age: 42 });
299}
300```