Let's learn Wing in 5 minutes, shall we?
let myString: str = "hello";
let myNumber: num = 1234;
let myBoolean: bool = true; // or "false"
And those you've always wished your language will have:
let myDuration: duration = 12h; // 12m, 12s
let myArray: Array<num> = [ 1, 2, 3, 4 ];
let mySet: Set<str> = { "dog", "cat" };
let myMap: Map<bool> = {
"key1" => true,
"key2" => false
};
let name = "jane";
assert("hello, {name}" == "hello, jane");
To avoid interpolation:
assert("hello \{joe}" == "hello, {joe}");
let myJson = {
foo: 123,
bar: {
zig: ["zag"]
}
};
assert(myJson.get("foo").asNum() == 123);
assert(myJson.get("bar").get("zig").getAt(0) == "zag");
You can use structs to access JSON objects in a strongly-typed way (see next).
Structs represent data shapes:
struct Person {
name: str;
last: str;
phone: str?;
age: num?;
}
// structs can be extended
struct Employee extends Person {
id: str;
}
let emp = Employee {
name: "super",
last: "man",
id: "100",
};
Structs interoperate very nicely with Json
:
let json = {
name: "hello",
last: "world"
};
let p = Person.fromJson(json);
assert(p.name == "hello");
Struct.fromJson()
performs validation:
let json = {
name: "hello",
last: "world"
};
Employee.fromJson(json);
// runtime error: unable to parse Employee:
// - instance requires property "id"
It's common to parse JSON from a string:
let response = http.get("/employees/100");
let emp = Employee.parseJson(response.body);
assert(emp.name == "super");
let fn = (x: str) => {
return "hello, i am {x}";
};
Return type is optional for closures:
let getLength = (s: str): num => {
return s.length;
};
Is the same as:
let getLength = (s: str) => {
return s.length;
};
The inflight
modifier for closures indicates that a closure can run on the cloud. This is called
an inflight closure:
let handler = inflight () => {
log("i am flying!");
};
new cloud.Function(handler);
Use void
to indicate no return value:
let printName = (name: str): void => {
log(name);
};
Use let
to assign a value to a variable:
let x = 12;
x = 77; // ERROR: x is non reassignable
Use let var
to make the variable reassignable:
let var y = "hello";
y = "world"; // OK (y is reassignable)
The usual suspects:
for i in iterable {
break;
continue;
}
This prints 0
through 4
:
for i in 0..5 {
log(i);
}
This prints 0
through 5
:
for i in 0..=5 {
log(i);
}
if condition {
// code
} elif condition {
// code
} else {
// code
}
while condition {
break;
continue;
}
bring expect;
struct NumbersProps {
max: num?;
}
class Numbers {
numbers: MutArray<num>;
max: num;
new(opts: NumbersProps?) {
this.max = opts?.max ?? 10;
this.numbers = MutArray<num>[];
}
pub add(n: num): num {
if this.numbers.length == this.max {
throw "cannot add more numbers";
}
this.numbers.push(n);
}
pub sum(): num {
let var s = 0;
for n in this.numbers {
s += n;
}
return s;
}
}
let n = new Numbers(max: 3);
n.add(12);
n.add(3);
n.add(3);
expect.equal(n.sum(), 18);
If the last argument of a function is a struct, it's fields become keyword arguments:
struct Options {
prefix: str;
delim: str?;
}
let join_str = (a: Array<str>, opts: Options) => {
let delim = opts.delim ?? ".";
return prefix + a.join(delim);
};
assert(join_str(["hello", "world"], "!") == "!hello.world");
assert(join_str(["hello", "world"], "!!", delim: "/") == "!!hello/world");
let var x: str? = "hello";
// check if a variable has a value
if x? {
print("x has a value")
}
Nullish coalescing:
let y: str = x ?? "default-value";
Optional fields in structs:
struct Foo {
optValue: num?;
}
Optional arguments in functions:
fn my_func(x: str, y: num?) {
// code
}
enum Animal {
DOG,
CAT,
MOUSE
}
// then
let x = Animal.DOG;
assert(x == Animal.DOG);
Standard library modules:
bring cloud;
bring fs;
bring util;
bring http;
bring math;
bring expect;
bring ui;
new cloud.Bucket();
Bring types from a file in the same project:
producer.w
:
// "pub" is needed here
pub class MyType {}
consumer.w
:
bring "./producer.w" as p;
new p.MyType();
Bring directories (all the pub
types within the directory and subdirectories can be accessed):
subdir/file1.w
pub class MyClass {}
subdir/foo/file2.
pub class YourClass {}
consumer.w
:
bring "./subdir" as s;
new s.MyClass();
new s.foo.YourClass();
Winglibs are 3rd party libraries that are published by the
Wing project to the @winglibs
npm scope and used like this:
npm i @winglibs/bedrock
Then:
bring bedrock;
new bedrock.Model("anthropic.claude-v2");
let fn = () => {
throw "message";
};
try {
fn();
} catch msg {
log("error: {msg}");
}
See Inflights in the Wing docs.
Use the inflight
modifier on methods to indicate that this method is part of the inflight API of
the object:
class MyStorage {
b: cloud.Bucket;
new() {
this.b = new cloud.Bucket();
}
pub inflight write(data: str) {
this.b.put("data.txt", data);
}
pub inflight read(): str {
return this.b.get("data.txt");
}
}
let storage = new MyStorage();
inflight () => {
storage.write("hello");
assert(storage.read() == "hello");
};
By default, classes can only be instantiated from preflight context:
inflight () => {
new Numbers();
};
// ERROR: Cannot create preflight class "Numbers" in inflight phase
To define inflight classes, use the inflight
modifier:
inflight class Bang {
// all members are inflight
}
inflight () => {
new Bang();
};