Comments (23)
-x could just be 0-x or neg(x) or x.neg
Simplicity and consistency are major goals here. That will inevitably leave some verbose corners.
from bosatsu.
so my new proposal is that operator ++
is exactly like concat
for instance. It can appear anywhere a binding name can:
operator ++ = foo
[1] ++ [2]
def takeOp(operator /, baz, bar):
baz / bar
match x:
operator f: f(1)
etc....
So, operator
is a key word that allows us using operator names as binds.
Then next part is that an operator without that prefix f +
is parsed as Apply(Operator("+"), f)
.
The last part is that we will limit operators to avoid :
_
,
@
(
)
and .
so we don't have any ambiguity with other existing symbols.
I think with #192 it should be pretty easy to implement this (although doing that refactor was a brutal several hours....)
from bosatsu.
I have this idea last night:
we could allow operators to be file local only:
package Foo/Bar
operators [
+ as plus_Int,
++ as concat_List,
prefix ! as not,
]
three = 1 + 2
three_list = [1] ++ [2] ++ [3]
false = ! True
Pros for operators:
- syntax gets out of the way and the semantics are easier to see.
- (minor) easier to write.
Cons for operators:
- readers don't always know what they mean (harder to google for generally).
- seen as encouraging a bespoke style that is difficult to transfer.
This proposal increases readability (all operators are declared in file scope and linked to a method), at the cost of extra verbosity (we can't import an operator, we have to copy-paste common ones).
An additional idea to aid readability is to limit the set of symbols that could be operators. Something like:
core = oneOf [ + - * / | % ! ? < > & * + ^ ]
repeated = core+ # ++ ** // !!
diamond = <core|repeated> # <*> <+> <++>
With a limited menu, plus explicit mapping to functions, maybe this is a good place to land.
Lastly, a binding form would be good:
x += 2
``
could be expanded to:
x = x + 2
So, more generally, any operator with `=` appended is also a legal use of an operator, though not legal for definition. This is really doubling down on new-let-scope simulating mutable references however... which maayyybe is a bad idea.
from bosatsu.
cc @non
from bosatsu.
actually, out of band, @avibryant suggested reusing the import binding syntax:
package Foo/Bar
import Baz [ foo as operator / ]
import Bosatsu/Predef [ plus_Int as operator + ]
So this introduces operator
and probably prefix operator
and postfix operator
inside the import statement.
It also highlights we need to be able to do rebinding of Bosatsu/Predef.
from bosatsu.
@snoble thoughts on this?
from bosatsu.
The challenge here is that if we allow totally unbounded syntax as operators, how do we parse them? Maybe there is something elegant, but I don't know what that might be.
Alternatively, we could have a fixed universe of possible operators, and then use the binding syntax we describe here. In this way, the parser does not need to know about what operators are in scope to parse, but of course we could have a name error later.
from bosatsu.
A simplifying requirement would be that all operators are postfix, and due to currying, they are all functions on one variable. So 1 +
is parsed as OperatorApply(Literal(1), Operator("+"))
Note, in this world, .
could be regular operator: a.f
== f(a)
which binds tighter than function application (so a.f(b)
== f(a)(b) == f(a, b)`).
This means the few prefix operators can't be easily supported, but I don't want to go crazy with operator syntax. Again a core goal is you can understand any file by reading it.
If we force all operators to be functions, (maybe curried) then I wonder if we can just use regular syntax to define them, the difference is, they have different call syntax.
external def +(left: Int, right: Int) -> Int
then we could import Bosatsu/Predef [ + as plus ]
if we wanted to opt out.
This is somewhat of a reversal of the idea that operators are second class and that we wanted to tax the writer by explaining them at the top of each file.
what do you think @snoble ?
from bosatsu.
This feels really compelling. Just thinking of some edge cases. Can an operator still be treated as a regular variable? Can you do f(+)? Can you have multicharacter operators (eg >> would be nice for composition)? If so, how do you prioritize how multiple characters are combined?
Can you use a let to assign a function to an operator?
from bosatsu.
Very good questions.
Maybe there could be some notation to treat an operator like a regular value. Without that notation, then we treat it as autoapplying.
operator(+) = plus
z = 1 + 2
def operator(-)(x, y): x.minus(y)
f(operator(+))
from bosatsu.
Or maybe no parens
import Foo [ bar as operator % ]
operator + = plus
f(operator +)
from bosatsu.
That's looking pretty neat. And I guess that solves how to think of multicharacter operators. If you Operator apply to a value assigned to an operator you first have to use the operator keyword. So multiple operator characters in a row are always treated as a single variable name
from bosatsu.
yeah. I think that's right. I actually really like this proposal at this point. I think it is simply:
operator
is a keyword that allows the following symbol to be used anywhere a let bound name would normally be used.- without the
operator
prefix, an operator is parsed as applying the previous value to the value bound to the operator.
The only open question in my mind is prioritization. My first inclination is no prioritization: strict left to right. Use parens if you don't want that rule.
My fallback is a fixed prioritization algorithm to enable *
-like things to bind tighter than +
like things and ||
like things to bind looser than &&
like things.
from bosatsu.
This is Ruby's precedence ordering
https://ruby-doc.org/core-2.2.0/doc/syntax/precedence_rdoc.html
from bosatsu.
I forgot about unary operators. Is it resonable a symbol could be assigned a different function for when it's applied as a unary?
from bosatsu.
parsing it a bit tricky with unary operators. Unless we have a fixed table that we know they are unary.
a~b
is that (a ~) b
or a (~b)
.
I almost want to prevent any unaries.
from bosatsu.
By my count python only has not
and ~
as unary operators. And only **
has right to left associativity.
Iām very inclined to say no unary operators and always left to right associativity.
Iām very close to implementing this proposal and seeing how it feels. I have to admit working with methods on integers is pretty annoying.
from bosatsu.
Shoot, I think that's right. I was trying to argue the opposite but then I got to trying to figure out what happens if you get two operators in a row, and I can't think of how to disambiguate in a way that doesn't seem broken.
Either you can support 2 + >> f
(where both are binary operators) or you can support 2 + - 3
(where the first is binary and the second is unary).
It might not be crazy to just disallow both. So you can't have an operator directly following an operator. So the first would have to be written as (2 +) >> f
and the second as 2 + (- 3)
With this restriction you can say an operator is parsed as unary iff it begins a statement. So your example would be parsed as (a ~) b
. Which seems reasonable since I think you need parentheses for non-operator applies
Not sure this is good. But I'm assuming users would like to write a = - b
for example
from bosatsu.
This doesn't solve how you could give an operator a unary and a binary definition... And maybe that's a good reason not to have unary operators
from bosatsu.
One idea after #194 and the idea that the unescaped names should be used for equality, we could just use backticks for operators:
`+` = add
maybe this is too terse, but I am not crazy about having two ways to say the same thing.... operator + = add
is a bit nicer to read, though... It is easy enough to support both.
from bosatsu.
It is true that foldLeft(0, +
) is pretty satisfying compared to typing out operator. But I don't have the instinctive aversion to having multiple ways to do things
from bosatsu.
An interesting trick scala does is any operator that ends with :
is right associative. So you can write: a :: b :: c ::: Nil
and build a list from right to left. That right association can be nice. It is something to consider that was not put in place in #198
from bosatsu.
Oooooh... I've wondered about that in Scala in the past. And that would be helpful in faking the tuple syntax
from bosatsu.
Related Issues (20)
- > ``` HOT 2
- changing how we compile/render values HOT 2
- Improving pattern compilation HOT 1
- TotalityTest.subset consistency flake
- flake in Kind test: we enumerate from most to superkinds HOT 1
- improve TypedExpr.substitute and Declaration.substitute
- migrate to ff4s HOT 1
- design for bosatsu libraries HOT 3
- StringSeqPatternSetLaws.relate consistency flake HOT 3
- Some types seem somewhat unusable HOT 2
- parsing external def doesn't allow using explicit type parameters
- syntax to require recursions to be tail recursive. HOT 1
- add a todo function when running type check
- using an unimported type gives a bad error message HOT 1
- change type-check to just check
- use parallelism and streaming in bosatsu test HOT 1
- python code generation has a bug
- Use cats-effect in core HOT 1
- Add a constant access time Array type
- Add Int32, Int64 and Int8 types
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
š Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. ššš
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ā¤ļø Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from bosatsu.