Skip to content

Commit 2173764

Browse files
committed
Misc. syntax changes
Signed-off-by: Nick Cameron <[email protected]>
1 parent 8bb8240 commit 2173764

File tree

1 file changed

+116
-0
lines changed

1 file changed

+116
-0
lines changed

syntax.md

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# Syntactic improvements
2+
3+
This doc proposes a bunch of improvements with the goal of a simple, cleaner, more intuitive syntax. Note that some of these changes go beyond just syntax changes though the effect is a cleaner syntax.
4+
5+
Currently, the first impression of KCL is poor - it feels very noisey with a lot of punctuation and symbols with non-obvious meanings and which are hard to search for.
6+
7+
For example, consider this sample
8+
9+
```
10+
lugHoles = startSketchOn(lugBase, 'END')
11+
|> circle({
12+
center: [lugSpacing / 2, 0],
13+
radius: 16 * mm() / 2
14+
}, %)
15+
|> patternCircular2d({
16+
arcDegrees: 360,
17+
center: [0, 0],
18+
instances: lugCount,
19+
rotateDuplicates: true
20+
}, %)
21+
|> extrude(-wheelWidth / 20, %)
22+
```
23+
24+
A newcomer might question:
25+
26+
- What does `|>` do?
27+
- What does `'END'` mean? Why is it quoted?
28+
- Why doe I need `{}` in `Circle` but not `startSketchOn`
29+
- What is `%` for?
30+
- Why does `mm()` have the `()` and why is it multiplying the number
31+
- What is `[]` for when using `center`
32+
33+
In other cases there are also `$` to declare tags, function declarations which are noisey (e.g., `fn spoke = (spokeGap, spokeAngle, spokeThickness) => { ... }`) - why is `=` and `=>` needed? Why does this look different to the std lib docs (e.g., `angledLineOfXLength(data: AngledLineData, sketch: Sketch, tag?: TagDeclarator)` - note no `=`, `: type` on arguments, and note that the `{}` has different semantics to those in the snippet). Points which are a key primitive are sometimes declared as `{ x: 1, y: 0, z: 0 }` and sometimes as `[0, 1, 2]`, and so forth.
34+
35+
This doc proposes some small changes. Other related changes are suggested in other docs:
36+
37+
- Changes to pipeline syntax and removing `%` in [pipelines and tags](pipelines.md)
38+
- Using `=` instead of `:` to initialise object fields in [type system](types.md)
39+
- Leading underscores (including just an underscore) are variables which can't be used in [construction geometry](show.md)
40+
41+
## Function declarations
42+
43+
Remove `=` and `=>`. E.g., `fn spoke = (spokeGap, spokeAngle, spokeThickness) => { ... }` becomes `fn spoke(spokeGap, spokeAngle, spokeThickness) { ... }`.
44+
45+
See [type system](types.md) for adding type annotations. See [functions](functions.md) for changes to organising and calling functions.
46+
47+
## Remove anonymous objects
48+
49+
The main use case was for function arguments. In general, named objects are better.
50+
51+
## Point and vector types and constructors
52+
53+
We introduce `Point2D`, `Point3D`, `Vec2D`, and `Vec3D` objects for absolute and relative points in space, respectively.
54+
55+
The constructor functions `pt` and `vec` produce these objects, depending on the number of arguments. E.g., `pt(0, 0, 0)` produces an object `Point3D { x = 0, y = 0, z = 0 }`. This facilitates access using `foo.x` or `foo['x']`, the latter useful for iterating over coordinates.
56+
57+
Later work: where the type is unambiguous (see [type system](types.md)), the user can just write `(0, 0, 0)` and KCL will infer whether to use `pt` or `vec`. I'm not sure if this desugaring should be made extensible or limited to the point and vector types.
58+
59+
## Use constants instead of magic strings
60+
61+
E.g., `XY` instead of `'XY'`.
62+
63+
Remove use of magic strings such as `END` for identifying geometry (see [tagging](TODO) for a proposed alternative).
64+
65+
## Example
66+
67+
Repeating and combining the initial examples:
68+
69+
```
70+
fn spoke = (spokeGap, spokeAngle, spokeThickness) => {
71+
...
72+
}
73+
74+
lugHoles = startSketchOn(lugBase, 'END')
75+
|> circle({
76+
center: [lugSpacing / 2, 0],
77+
radius: 16 * mm() / 2
78+
}, %)
79+
|> patternCircular2d({
80+
arcDegrees: 360,
81+
center: [0, 0],
82+
instances: lugCount,
83+
rotateDuplicates: true
84+
}, %)
85+
|> extrude(-wheelWidth / 20, %)
86+
```
87+
88+
would become
89+
90+
```
91+
fn spoke(spokeGap, spokeAngle, spokeThickness) {
92+
...
93+
}
94+
95+
lugHoles = startSketchOn(lugBase.faces.end)
96+
|> circle(
97+
center = (lugSpacing / 2, 0),
98+
radius = 16mm / 2,
99+
)
100+
|> patternCircular2d(
101+
arcDegrees = 360,
102+
center = (0, 0),
103+
instances = lugCount,
104+
rotateDuplicates = true
105+
)
106+
|> extrude(-wheelWidth / 20)
107+
```
108+
109+
Notes
110+
111+
- `lugBase.faces.end` is a strawman for finding geometry, requires work
112+
- `16mm` relies on work on units of measure, assuming mm is the default unit for the project, just `16` should work
113+
- `center = (0, 0)` could also be written as `center = pt(0, 0)`
114+
- fully annotated with types, the function might look like `fn spoke(spokeGap: Number, spokeAngle: Number, spokeThickness: Number): Solid` or `fn spoke(spokeGap: num, spokeAngle: num, spokeThickness: num): Solid` or `fn spoke(spokeGap: Number(1d), spokeAngle: Number(angle), spokeThickness: Number(1d)): Solid`, we wouldn't need any other type annotations in the language.
115+
116+
I believe this keeps the spirit and advantages of KCL while making the syntax friendlier and more attractive.

0 commit comments

Comments
 (0)