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
Compiler signature re-work #1116
base: main
Are you sure you want to change the base?
Conversation
f3512ec
to
d31af8d
Compare
Updated, for functions of 0 arity that where also enabled as |
It has only one user, so fusion it.
d31af8d
to
bd4c396
Compare
Updated: found another leaked symbol from the compiler, while tinkering on other things. |
bd4c396
to
f211692
Compare
Updated: Some minor renaming of functions. |
f211692
to
93db3d3
Compare
As far as allowing functions with zero arity is concerned, am I right in thinking that there would be no essential difference between: var f = Fn.new { || null } and var f = Fn.new { null } In effect the first would be a variation of the second and you'd call them both with However, I'm struggling to get my head around an indexed property with zero arity such as |
For your first point, it is equivalent (for now). Thought with the new system, it would be possible to distinguish between them and all For the subscript change, I already proposed it a while ago, but is now a easy by product of the simplification of how all the list are handled. It simplifies the syntax, by making objects callable as functors (without the strange arity restriction, or the need to use the class Set {
static [] { this[Object] }
static [clazz] { ... }
}
var ObjectSet = Set[]
var NumSet = Set[Num] One neat idea would be to move some of the boiler plate to the compiler, so we can have the syntax: class Set[clazz = Object] {
} but I don't know how hard it would be to add default arguments without a proper AST. And in addition, we would need to support it done for functions and methods in the same time, so for now it is a no go but we can produce at least an ersatz of it. |
93db3d3
to
0b9d8ff
Compare
Updated: Make it |
Thanks for clarifying those two points. I agree that it would not be a good idea to have a function getter since, unlike classes, functions don't have any persistent internal state (they just use global state) and it would break tons of exsting code in any case. It hadn't occurred to me that one could use an indexed property with zero arity as a default for other indexed properties but, yes, I can see the sense in that now. Your idea of using static indexed properties to create a sort of template class is also interesting. |
Well technically functions have a state because they are technically closure. So they can have private states, hidden from the global environment. So technically, I think it would make sense to have a getter function that would honor the singleton pattern. That said, unless there is more solid ground I don't consider it an ultimate reason to break code. Well this rule is stupid in the first place (even if C++23 only adopted more than one operator). While semantically it has a different meaning, it makes absolutely no reason to differentiate it from a function call operator. I simple term, it is a language rule for the sake of a rule because of some historical usage and connotation. Nothing in the grammar rules forbids the extension, if we are willing to ignore the connotation. I mean what ever the language, you can't prevent users from inventing DSL. If using class Foo {
static call() { "0-spread()" }
static call(a) { "1-spread(%(a))" }
static call(a, b) { "2-spread(%(a), %(b))" }
static call(a, b, c) { "3-spread(%(a), %(b), %(c))" }
}
class SubcriptSpreader {
construct bind(callable) {
_callable = callable
}
[args] {
var count = args.count
if (count == 0) return _callable.call()
if (count == 1) return _callable.call(args[0])
if (count == 2) return _callable.call(args[0], args[1])
if (count == 3) return _callable.call(args[0], args[1], args[2])
}
}
var s = SubcriptSpreader.bind(Foo)
System.print(s[[]]) // expect: 0-spread()
System.print(s[["a"]]) // expect: 1-spread(a)
System.print(s[["a", "b"]]) // expect: 2-spread(a, b)
System.print(s[["a", "b", "c"]]) // expect: 3-spread(a, b, c) And this is true for every function/method overload in existence... |
Whilst functions in Wren are closures, they close over variables defined outside their scope. Even if all a parameterless function did was to return a closed over variable, I don't think a user would regard this as analogous to a 'getter' method returning a field value from a class. But, minor quibbles aside, I do think this proposal as a whole hangs together well and I would therefore be in favor of it even if (as I said at the time) I'm not keen on #1112, preferring the simplicity of overloading methods purely on arity. |
In some near future this change set will also relax a little the grammar of attributes (because of code reuse). It means attribute group will be able to be empty, and attributes group will be able to be ',' terminated like every other lists in the language with this changeset. |
Doing `Code` arithmetic at many place is not really wise.
0b9d8ff
to
38b1ec3
Compare
Updated, changes are a little bit more atomic, added type hinting support. |
As far as 'type hinting' is concerned, if I'm understanding that correctly, you've just laid the groundwork for:
These hints will be ignored by the compiler and we'll need to agree 'best practice' for what type should be shown particularly as far as sum types, lists, maps etc. are concerned. Although it's not a built-in type, one thing I'd personally like to see in such hints is the use of 'Int' to represent a Num which is an integer given how common this situation is. |
For your last concern, we can always add |
Just a further point on the type hinting. All the discussion I've seen so far has used a colon (rather than a crow's foot) to introduce the return type as well as the parameter type(s). Having said that, Python uses the crow's foot for the return type though that may be because they use a colon to introduce the function block. Any particular reason why you prefer the crow's foot? I'd have thought myself that it would be better to keep the number of new symbols needed to a minimum. |
I was not able to reuse
Does it means On the side note, I'm having some small difficulties to finish patch-set, so it will take me some time to produce some good quality changes. I had to fix some errors related to the GC with WREN_DEBUG_GC_STRESS to test memory issues. This one is almost properly sorted, thought some cruft is left related to attributes. But that lead me too look at attributes, and oh boy... The attribute implementation tries too hard and produce/use a lot of unnecessary junk. Instead, I think we should bypass the constant system responsible of the memory usage reduction (since we can't store a Map as a key of a Map). That way we can simply store the compiler attribute maps directly in the constant pool, instead of serializing them in opcode to recreate them at execution. I have done a changeset for that but I need to clean/polish it. |
TBH I'd missed that a relaxed grammar for setters was part of the proposal. Wouldn't it be better for the parameter list, however many there are, to always be enclosed in parentheses? That would be consistent with the current syntax, easy to remember and would mean that we could then use a colon to introduce the return type hint. |
From a grammatical point of view, only the The same could be said for binary operators, and could have the same treatment of optional parenthesis (That makes me realize I missed type hinting for them, but this trivial to implement since the infrastructure is now available). Here are some file size numbers on my machine:
Considering all the moving parts my change set brings, some of security/language improvement it brings (and probably some missed optimization opportunities), I consider that non explosion of size by factorization quite successful for now. |
Yep, the file size numbers look good but would presumably be even better if you didn't have to support two syntaxes (with and without parentheses) for setters. Even if they're not really needed and it's more typing, I'd personally prefer parentheses to be mandatory both on consistency grounds and because of the type hint issue. There's also #849 to consider which I wouldn't be surprised to see implemented in some shape or form. If it is, then the most likely syntax (as it's short and sweet) would be: |
Updated the header, to mention the relaxed setter declaration syntax. In the facts, I would go the other way around and would consider generalizing About the #489 I don't think the syntax |
Well, I think the point of #849 was to be able to condense: class Entity {
construct (pos) {
_pos = pos
}
pos { _pos }
pos=(v) { _pos = v }
} into just: class Entity {
construct (pos) {
_pos = pos
}
pos = _pos
} The current way of doing things would still be available for read only or write only properties or where you needed to do more than just get or set a field. So I think it is a viable proposal even if I personally would probably not use it much. |
Incidentally (and going slightly off topic), I made an even more condensed proposal in #912 for simple classes which only had read-only fields (tuples in effect) where you'd get a default class Entity(pos) though it wasn't met with much enthusiasm :( Nowadays, I just create named tuples dynamically using the Meta module. |
While still beeing off topic, I'm really considering to borrow most of the code you made public in rosettacode and making it a proper standard library of some sort... |
Well, you're very welcome to do that :) I'm afraid there's no separate documentation for the various modules though I've tried to describe each method etc. within the code itself. |
While doing some crazy things with "template" programming, I felt into a trap while trying to emulate some method overload. And I think, I need to also allow something that would be named like subscript caller. Basically it means to allow:
Luckily, it should be pretty simple to implement, with my current change set. Thought, there is a little friction with the function syntax. Basically, by making
I think it should be a map as per This simplification rabbit hole is so deep... lets sip some tea at the very bottom Alice. |
Now, that I wrote it maybe it is non sense. I need to sort things and maybe simply ban |
If I were you, I wouldn't try and make |
Can you expand your thought. I'm not sure to understand what you mean. In particular, by:
Do you mean, I should not allow the |
I mean that I'd allow |
Well it is not nice in the case of what I'm trying to achieve. So maybe it will go in a late patch-set to make it not mandatory. Anyway |
Well, I'll grant you that it's pseudo-functional already in the sense that functions are first class and we have But I think you can go too far down that path and, when you start making things optional, it means you have to make a decision each time you use them which option to go for. |
I think it is more than a trend. If you look at smalltalk, there are lambda/closures every where hidden in plain sight in the form of innocent looking blocks. And if we look more carefully to the smalltalk syntax, the notion of block only exist at the top level of a script, the method body declaration or the lambda body declaration. All the control flow does not use block at all, it is a straight line from the begin of the block to its end, with control flow being simulated via method dispatch. So trying to ban functional style is waste of time... It is more trending today, probably because the popular kids in C++ town, made it more accessible via the lambda syntax. But the men where already using it with dedicated functor objects (which was a lot of boiler plate). An considering how C++ is influential, it snow balled from there. Even in C, the call back function (re-)learning and its wide adoption showed how important that notion is. (And guess what I did exactly that to implement lists handling in the change-set) |
Do you have any idea of what the following line mean? Line 2060 in c2a75f1
Was it meant to allow to do stuff like; Foo.foo(a, b,...) bar (c, d,...) baz (e, f, ...) ..
Foo.foo { /* a function */ } bar { /* a function */ } baz { /* a function */ } ... |
Had to Google it as I'm not familiar with the Grace programming language but apparently it supports multi-part (or 'mixfix') methods. I found this example: which is a call of the So I think your guess at what it means is correct though why we would want to support something like this in a simple language such as Wren I don't know. |
The way I presented it, as composed signature should be less problematic, a nice tribute to Smalltalk invocation style, and probably an alternative solution to #1112. I'll probably give it a try, but it is not my highest priority. For now I only want to have |
This patch set propose to rework how
Signature
works.The key changes in behavior:
SignatureType
is removed, so it simplify a lot of logic by storing thesubscriptArity
,parenArity
,isInitializer
andisSetter
instead.It contains some simplifications to
signatureToString
that should be very handy to implement [RFC] Support argument labels as part of method signatures #1112.