Open aviRon012 opened 1 year ago
I'm cautiously optimistic about this direction. I don't think this is something we should push for in Carbon 0.1, but it seems like an interesting avenue to explore post-0.1.
Notice that if the above functions can be declared as methods of Polygon, then the above can be expressed in terms of chaining methods [...] however this is not always appropriate to define functions as methods of classes.
self
, and the decision is made by the method not by the caller.We could also allow functions to be treated as methods, with the %
sigil used to indicate which parameter is treated as self
:
let transformed_polygon: auto = polygon
.(scale)(%, 5.0)
.(rotate)(%, 90.0)
.(translate)(%, 3.0, 5.0);
... but I don't find this syntax especially aesthetically pleasing, and it would be a special case rather than a natural consequence of our other rules.
Passing the components of a tuple as arguments to a function
The %[i]
syntax here makes sense. To forward a tuple as function arguments, I think we may not need to invent anything new: returns_tuple() |> takes_multiple_args(..., [:]%)
seems like it should do the right thing.
If we do decide to implement this feature, a nice mental model for this is an analogy to a calculator.
With a calculator, you write a mathematical expression, and then you press =
, and the result of the calculation is stored in a variable you can refer to as ans
.
So, in Carbon you write an expression, then you write |>
(like pressing =
), then you write a proceeding expression using %
(the same way you use ans
).
To forward a tuple as function arguments, I think we may not need to invent anything new:
returns_tuple() |> takes_multiple_args(..., [:]%)
seems like it should do the right thing.
For the benefit of other readers: ...,
and [:]
are part of the current early draft design for variadics. To vastly oversimplify, [:]
transforms a tuple into a pack, and ...,
turns a pack into a comma-separated list. This specific syntax is very much a work in progress, but I expect the final design for variadics to still need two separate operators to do this, and although their spelling and fixity may change, I don't expect them to get much more concise.
less symbol more readable
Some thoughts: is this just syntactic sugar, or a lead into FP?
data Polygon = Poly
scale :: double -> Polygon -> Polygon
scale s p = undefined
rotate :: Double -> Polygon -> Polygon
rotate a p = undefined
translate :: Double -> Double -> Polygon -> Polygon
translate x y p = undefined
{-
transformed_polygon: auto = polygon
|> scale(5.0)
|> rotate(90.0)
|> translate(3.0, 5.0);
-}
-- either with composition
x1 = ((translate 3.0 5.0) . (rotate 90.0) . (scale 5.0)) Poly
-- or with right associative application
x2 = translate 3.0 5.0 $ rotate 90.0 $ scale 5.0 $ Poly
There are some inconsistencies that should be addressed
let x1 : auto = polygon |> scale(%, 1.2);
let x2 : auto = polygon |> fn (p){ return scale(p, 1.2)}; // some lambda instead of a function
let lambda = fn (p){ return scale(p, 1.2)}; let x3 : auto = polygon |> lambda
fn getlambda() -> auto {return fn (p){ return scale(p, 1.2)};} let x4 : auto = polygon |> getlambda();
fn getpartial() -> auto {return scale(%, 1.2)}; let x5 : auto = polygon |> getpartial();
We also have some potential for ambiguity in this case:
fn foo(p: i32) -> i32 {return p * 2;}
fn example() { let foo = () { return 3;} // can currently give variable same name as a function let x = polygon |> foo // ah shucks, which foo? }
3. If `|>` is a simple 'chain output to first parameter' What advantage is there in adding `|>` syntax over reusing `.`
```carbon
// if "." is relaxed such that a.f is applies function f with a as first argument, then we need no more
// some struct with private internals
let x : PrivStruct
fn foo(y: Polygon) -> PrivStruct
fn bar(p: PrivStruct, x: i32) -> PrivStruct
polygon. scale(0.3).foo().privStructMethod(3).bar(4)
|>
is implemented as function application (necessitates support for partial application), then
this feels like a proposal that would work better as a larger FP extension to the language.
let trans : auto = scale(%, 0.5) => rotate(%, 90) => translate(%, 3, 2);
polygon |> trans |> printMe
let x: auto = [1,2,3,4]
let y: auto = [polygon.clone() for _ in 0..x.len()]
zip(y,x).map(scale) |> printMe
Summary of issue:
Support a pipeline operator
|>
to pass the output of one function as parameters to another function, This is a feature that exists in many languages, And it can improve code readability.Details:
Examples
Say we have some functions:
Now compare the difference in readability between the following two code examples:
I think most would agree that the first option is more readable. Another example is with iteration (loosely):
The alternatives aren't great
Notice that if the above functions can be declared as methods of
Polygon
, then the above can be expressed in terms of chaining methods:however this is not always appropriate to define functions as methods of classes. Another way to solve this is with temporary values, the output of each function is saved in a temporary value and pass that value to the next function
This too is not great.
Alternative syntax
We can have a symbol such as
%
that will be placed where arguments are normally passed to a function, this has the advantage of being able to specify to which argument of next function will the output of the previous function go:Passing the components of a tuple as arguments to a function
Sometimes functions return multiple values as a tuple, It may be desirable to pass those multiple values as arguments to the next function, if so there needs to be a way to differentiate between passing the tuple and passing its components, perhaps with an operator
||>
or|>>
or something similar, or perhaps unpacking tuples can be solved by some other mechanism of the language, or if we go with the option of the%
symbol perhaps we can use the following syntax:or perhaps this syntax:
Other languages
Any other information that you want to share?
No response