-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pipelines, tagging, and for loops #15
base: main
Are you sure you want to change the base?
Conversation
pipelines.md
Outdated
|
||
#### Alternative | ||
|
||
Retain `#` or replace it with a keyword (which I would prefer) for the rare cases where it is required (c.f., the current situation where it is frequently required). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could it be optional? Something like the value is passed as the first parameter, syntactically. But if you specify %
(or your favorite keyword), then it's substituted there instead. (Omission is still disallowed.) This would allow pipelines to be used in more contexts. In contrast, if it must be the first argument, I might need to split up a pipeline just to call something where the argument is in different a position. One common example is a function that accepts two parameters of a type, where they're not symmetric.
The way it's proposed, it's F#-style pipelining. On the other hand, I believe %
was inspired by the JS proposal which borrows from the Hack lang.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer not to, if it's not needed often. It's one more thing to learn and one more thing for beginners to see and get confused by. I believe any remaining requirement can be met by factoring out a variable, possibly using as
to avoid splitting the pipeline (and using a variable is self-documenting in the way that %
is not).
pipelines.md
Outdated
|
||
Or we could have a mandatory braced block *and* either `|>` or `in`. | ||
|
||
And/or, we could use `for i in [...]` rather than `for [...] as i`. This is closer to other PLs and reads better, however, it does not reuse the `as` keyword and so requires a new keyword. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using a new keyword doesn't bother me. I guess it can make the parser implementation more complicated because it might mean that we need to do more contextual parsing. (For import
, we ended up checking the following character in the tokenizer -- import
vs. import(
-- and that felt dirty. Would have been cleaner, IMO, to check the following token in the parser.) But I think it's worth it for simple things like this.
I think the reason to go with as i
is so that users have one fewer thing to learn. When they learn it in for
loops, it's training them to use it elsewhere. Then again, anyone who's programmed before gets for i in
, so that could probably work too. Personally, I find as i
more elegant and consistent as a design.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should fix the import thing to work on tokens in the future (although actually I think we should make import
a full keyword once we remove the function from std). I don't mind contextual keywords too much and I don't mind adding a few more keywords in general, but a langauge with a lot of keywords can be overwhelming, so there is a trade off in the 'big picture'.
The more I think about this, the more I prefer in
, I have to say
pipelines.md
Outdated
|
||
### Functions may have an explicit receiver | ||
|
||
In the declaration, this is the first argument of a function and must use a distinguished keyword (most PLs use `self` or `this`) or symbol (we could reuse `#` either alone or as a decorator on a name, e.g., `#sketch`, or any other sigil). I prefer a keyword, I have no preference which. Note that in other PLs the receiver is used for dispatch of a method, not just the syntax of calls. That is not proposed yet. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the benefit of using a distinguished keyword? In Haskell, Elm and Elixir (the only languages I've personally used with this feature) this is implicit for all functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which keyword? The receiver here is this
in Java or self
in Rust. You need something to name the parameter inside the function, you could use %
, but I think using self
or whatever is better
pipelines.md
Outdated
|
||
### Functions may have an explicit receiver | ||
|
||
In the declaration, this is the first argument of a function and must use a distinguished keyword (most PLs use `self` or `this`) or symbol (we could reuse `#` either alone or as a decorator on a name, e.g., `#sketch`, or any other sigil). I prefer a keyword, I have no preference which. Note that in other PLs the receiver is used for dispatch of a method, not just the syntax of calls. That is not proposed yet. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that this should replace the first arg, not the last like in some other language I'm more familiar with. I think @Irev-Dev proposed using the last param as the pipelined argument, but that makes it harder to add new optional arguments, which let us extend the stdlib without breaking old code.
|
||
This syntax is easier to read and comprehend, extends to user-defined functions, and reduces the total feature count of the language (since it is already used in imports). | ||
|
||
Example: `foo = ... |> bar() as baz |> qux()`, here the final result is assigned into `foo` (no change to semantics), the result of executing `|> bar()` is assigned into `baz` (equivalent to `|> bar($baz)` in the current syntax). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What scope is the variable placed in? Can baz
be referenced outside the pipeline? If so, I assume they're scoped to the same scope the pipeline is in?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes and yes. It's basically sugar for breaking the pipeline and using baz = ...
, so the same scoping etc.
|
||
``` | ||
startSketchOn('XZ') | ||
|> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this basically an anonymous function that takes no arguments except an implicit this
or self
or whatever first argument that gets elided due to the pipeline?
If so, maybe we should reuse the anonymous function syntax rather than having 2 ways to do something very similar? But maybe the readability improvements of this block are worth it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but with the addition of having the subject of the pipeline passed to the last expression of the block.
I'd prefer not to reuse the anon fn syntax here, it would be really noisy. I would like to think a bit about what functions are and how the user thinks of them and how much we rely on them (Jon has rethought this too in his constraints proposal). I'm kind of hoping we can get rid of anon functions entirely since they are a pretty hard to grok concept for non-programmers, but that's a bit of a tangent...
pipelines.md
Outdated
|
||
Syntax: `'for' expr_1 'as' id |> expr_2`. May appear within a pipeline or as an expression outside a pipeline. | ||
|
||
In the simplest case (with no preceding pipeline), `expr_1` is evaluated to some kind of an object `s` with sequence type (an array or range). `expr_2` is evaluated with each item in `s` bound in turn to `id`. The result of execution is an array of the results of evaluating `expr_2` (i.e., you can think of the expression as performing 'for each' or 'map' on its input). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK I'll be honest: I had trouble following this and figuring out the semantics being proposed, even after rereading it. I'll come back and read this again, but I worry that if I find it confusing our users will too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bear in mind this description is intended for implementers rather than users. I would start with the examples and ask what they suggest? Also maybe better to start with the for i in [...]
version rather than the as
version.
|> yLine(10) | ||
|> line([0.6, 0]) | ||
|> yLine(-.05) | ||
|> tangentialArc({ radius: 0.6, offset: -90 }) as arcSketch |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was chatting with @Irev-Dev about "temp" variables for points. It occurred to me that a user might actually want to see labels of points, lines, or anything in the scene. We could use the variable names. But that feels wrong to me since you can typically have lots of variable names in different scopes aliasing the same thing. What if as arcSketch
had the additional effect of being the canonical label of that piece of geometry? The user could toggle it in the UI. It's just an idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that seems the right way to work. We'd have to work out what to do about these variable names if they are then duplicated with a pattern of for loop or by using a function or whatever, but that's a topic we need to design out a bit in general, I think
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Proposal for improving the look and feel of pipelines and tagging, and adding for loops.