Skip to content
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

GDScript: Design goals #18698

Closed
vnen opened this issue May 8, 2018 · 39 comments
Closed

GDScript: Design goals #18698

vnen opened this issue May 8, 2018 · 39 comments

Comments

@vnen
Copy link
Member

vnen commented May 8, 2018

There are a lot of requests for changing stuff in GDScript, be it to extend functionality, make some syntax sugar, or add their favorite language feature. While it's good to know how people are using the language in order to improve it, deciding cases ad-hoc is prone to lead to inconsistency. It'll become more and more like a Frankenstein monster.

My proposal here is to create some master design goals for GDScript in the roadmap repository and decisions should be based on such document. That way there's a clear set of criteria to accept or reject proposals made by users.

This ticket is to ask about ideas on how to make such document, and what goals we should have in mind. This is not a place to ask for your wanted feature, this is more of "meta design", showing which way we should go in general, and not specific things that should go in and out of GDScript.

For instance, there are some decisions to be made:

  • GDScript should be kept as small as possible vs. embrace a large library of functions.
  • Whether it should have powerful operators that acts on different types, or it should be limited to mathematical concepts.
  • Add more Python-like statements, or go to a simpler route and add only what's hard/impossible to work around.
  • More functional vs. more procedural approach.
  • Only one way to do something vs. more shorthand constructs (AKA syntax sugar).
  • Whether to embrace a variety of coding styles and paradigms (being more welcome for experience from other languages) or define a more strict GDScript-way of doing things (being easier to collaborate with different coders).

And probably some other stuff that I'm forgetting.

The idea here is not to decide which features will go in (though that's the endpoint) but to make up which criteria we should use to decide the features.


These are some ideas that I gathered when I was writing a proposal for discussion. I'll leave them here as it is what I now have in mind:

  1. GDScript should be expressive but not bloated.
  • E.g. we probably don’t need a list comprehension syntax, but there should be an easy way to do it, in particular for making nested Arrays.
  • In fact, this relates directly to the Godot API. The more things available in the API, the less GDScript needs to do (also, it’ll be available for other languages as well).
  1. GDScript should try to be object-oriented, while allowing some functional paradigms.
  • This means that inheritance, polymorphism, encapsulation principles should be applied.
  • The language should provide first-class functions and implementation of common higher-order functions (such as map, filter, and reduce).
  • Re-use of functionality is also expected. So it needs constructs outside of inheritance to share code (such as Traits).
  1. GDScript should be simple to write, hard to do wrong.
  • Type specifiers are optional, but types should be strong.
  • The compiler must try to catch as many potential errors as possible, showing error and warning messages (in debug mode) for possible programming mistakes.
  • The compiler should make code optimizations based on inferred semantics, even when the programmer is being verbose. E.g. treating a local variable as constant if it’s never changed.
  1. GDScript implementation should be simple.
  • So more people can understand and contribute. We don’t really need a super optimized language, though we do want to have optimizations.
  • Syntax should be simple and fit an LL(k) grammar with a small ‘k’ value. I'm managing to assume LL(1) for the new parser, and so far it works. So I'm avoiding any syntax that increases lookahead (as this makes things harder to parse).
  • Reduce the number of builtin functions to a minimum (use the engine API for more things).
@NathanLovato
Copy link
Contributor

As a tutor the fact that GDscript is focused on gameplay programming at the moment is a great advantage. It does the job well yet it doesn't feel bloated and provided you're careful in how you implement things the code stays pretty easy to read.

I'd recommend to bring Lars Kokemohr in the loop: he's got a lot of gamedev experience, including AAA, thinks seriously about code architecture and languages, and he's got experience teaching Godot and Unity at university level so he's got great insights to offer.

@mhilbrunner
Copy link
Member

GDScript should be kept as small as possible vs. embrace a large library of functions.

As small as possible is exactly the crux of the matter, because as possible implies as possible while still fullfilling its purpose.

Also, these are different concerns: a large library of functions does not necessarily imply a complex language. And this is exactly where I would try to draw the line:

  • Try to keep GDScript, the language, as small as possible
    GDScript is hard to contribute to (and maintain), like the 3D renderer. Thus it scales badly with number of contributors. The larger and more complicated it becomes, the harder it is for others to improve upon it.
    Favor performance and simplicity (and ease of use and learning) over features. We can add nice-to-haves, but should be conscious about the cost in terms of complexity.

  • Favor adding methods to the standard library over additions to GDScript
    A large standard library scales way better than new operators or statements for GDScript.
    This is why I was in favor of adding e.g. new String utility methods in the past:
    In contrast to GDScript, these have no additional cost for us when making other changes; if I now add something somewhere else, I probably don't need to think about the implications of those String methods. But when we add things to GDScript, that influences every past and future contribution to GDScript, most of the time.

TL;DR:

  • Try to keep GDScript itself small, fast, easy to learn and maintain
  • Aim for a comprehensive standard library instead, Python batteries included style

@mhilbrunner
Copy link
Member

Add more Python-like statements, or go to a simpler route and add only what's hard/impossible to work around.
Only one way to do something vs. more shorthand constructs (AKA syntax sugar).

There should only be one (exposed and documented) way to do a thing, but that way should be as easy as possible.

Whether to embrace a variety of coding styles and paradigms (being more welcome for experience from other languages) or define a more strict GDScript-way of doing things (being easier to collaborate with different coders).

Keep one Godot way to do things, like there is a Pythonic way to do things.
Don't make other styles impossible unless it gains performance/simplicity/ease of use, but don't encourage them officially either.

@NathanLovato
Copy link
Contributor

Agreed with @mhilbrunner here. Especially on the:

Keep one Godot way to do things, like there is a Pythonic way to do things.

Regarding the library part it means focusing on improving, adding nodes and other built-in classes right? Doing so benefits other languages too so it benefits every developer.

@mhilbrunner
Copy link
Member

mhilbrunner commented May 8, 2018

@NathanLovato Exactly, nodes, classes and methods on them instead of keywords or statements in GDScript. Thats not possible for every feature; lamdbas or variadic functions need support in GDScript.

But when we can add something to the standard library instead, we should strive to do so.

@nobuyukinyuu
Copy link
Contributor

nobuyukinyuu commented May 8, 2018

Please make these things more consistent:

  1. The use of properties in lieu of getters and setters still leans heavily in favor of the latter. Usage could be reflective of the expected amount of impact or side effects reading or changing a value has, but it's not always clear.

  2. Constructor syntax is wildly inconsistent. Some types have functions which return an instance of it's type as a special overload of it's type name. Others use the .new() or .instance() syntax, types of which supporting this aren't always clearly documented. Yet others still use an initializer function with a void type on a recently created instance. Arrays and dictionaries have their own unique syntax.
    This all might be alleviated with function overload support, which may require better type hinting at gdscript's method signature level to implement, which brings me to...

  3. More consistent type hinting. Yes, gdscript is duck typed. But hinting was clearly favored in some places early on and it would be helpful for users to have explicit syntax for it everywhere vars can be declared, not just in exports. The syntax which already exists is already compatible with most use cases, save for explicit instancing because of the inconsistent ctor syntax. Maybe it's my inexperience with dynamic typed languages leading to my confusion. If Type-hinted exports can be Nil until explicitly initialized, Regular script variables seem to rely on determining its type at declaration based on assignment, and implicit type conversion behavior isn't yet clear to me. Getting back to the point, making this behavior more obvious will help make it easier – or possible, if it isn't already – to unify the syntax used to give a recently declared variable a value.

Hopefully none of these points ventured too far into feature request territory. Some things seemed necessary to implement to meet these design suggestions as a matter of course, but may not be necessarily reflective of the major contributors' design goals for the language...

Edit: remind me not to write these things half-asleep. Point 3 meanders a bit too much to make a strong case for itself, but basically it's a "furthermore" to point 2, to make both initialization and declaration of a var more consistent, which are two separate but heavily intertwined things.

@spaghiajoeojo
Copy link

In my opinion Gdscript has enough features right now and scripting with it is simple and fun. In the other hand GDNative is enough mature to counter gdscript lacks. If I need (?) lambda functions there's C# support, if I need fully functional languages I can bind my favorite one without trying to make gdscript like that.

TL;DR
Leave GDScript small and beautiful as it is. It is far better than 1.0 and do what it has to do.

@ghost
Copy link

ghost commented May 8, 2018

I'm gonna be a stinker and say that the syntax sugar added in for 3.0 were some of my favourite additions, and they were a big factor in my decision to pick up Godot. This includes both the shift towards interacting with properties directly, and the $ shorthand for get_node().

Don't get me wrong, I don't want to see GDScript bloated by unnecessary sugar out of principle. However, the additions so far have been really neat IMO and I'd love to see more shorthand constructs like these considered in the future. I just don't know where exactly to draw the line other than "let someone with good judgement have the final say."

@mhilbrunner
Copy link
Member

@Homer666 Have to agree. Those were nice changes for sure.

@dodgyville
Copy link

dodgyville commented May 8, 2018

I'm in favour of keeping it small and tight and with a single Godot way to do stuff (eg we should standardise on .new() or .instance() )

As it is already python-esque, when gdscript needs a new feature I think it should track python as closely as possible. It allows new users to transfer their skills across very quickly and the large python dev community have solved problems gdscript is likely to encounter as it grows.

Stuff I'd like to see: array slices, Array.extend, list comprehensions, optional keyword arguments, something equivalent to try except error catching, random.shuffle, random.choice.

@Zireael07
Copy link
Contributor

Multiple imports such as Python has would be handy, if we aimed to replicate basic Python features.

@Zylann
Copy link
Contributor

Zylann commented May 8, 2018

I miss functional things a little, especially lambdas which were proposed 3 times now but for some reason never went merged... maybe not enough time to review / big updates were coming at the same time? Functions not being able to be first-class citizens feels really odd in a dynamic language.
Also to me that's not a good reason to ditch the feature and move to C# (which is a very different beast), I believe GDScript can be really good in the way it was built in Godot.

Also, I miss the ability to await for a value, enumerate a generator, or perhaps a better yield, in order to deal with non-immediate code/functions. When you deal with requests, time-sliced processes, threads or even dialogs, using callbacks all over the place gets messy really quick because all vars need to be flattened as class members and you get more functions in the same scope, instead of a code flow that is easy to read (it gets even nastier when such things are nested).

Finally, optional typing can be really good for tooling and maintainance, but I think that's on the way :)

@reduz
Copy link
Member

reduz commented May 8, 2018

I mostly agree with @vnen. This is more a practical than a theoretical thing:

GDScript codebase needs to stay small to be maintainable. The more features we add, the more unmaintainable it becomes. Features that you may use only a few times in your project that will save 2 or 3 lines of code, should not be included.

If adding a cool feature that will be useful a few times only takes like, 1 or 2 lines of code.. this is fine, nothing against it. If adding it takes several dozen lines of code, then it will most likely be rejected.

Property access and the $ operator were features you use often that definitely improve quality of life. The upcoming optional typing is the same.. so even if they need a few dozen lines of code, they are worth it.

Lambdas may also be useful, but if it means hugely increasing GDScript size, then they should not be added.

The best approach that needs to be taken here to implement features is to do it using the least amount of new code possible.

@reduz
Copy link
Member

reduz commented May 8, 2018

Constructor syntax is wildly inconsistent. Some types have functions which return an instance of it's type as a special overload of it's type name. Others use the .new() or .instance() syntax, types of which supporting this aren't always clearly documented.

But they are different things. First is for built-ins, second is for classes, third is not really even a constructor. I feel if they were all the same, it would be more confusing.

@reduz
Copy link
Member

reduz commented May 8, 2018

I think it should track python as closely as possible. It allows new users to transfer their skills across very quickly and the large python dev community have solved problems gdscript is likely to encounter as it grows.

This is great in theory, but in practice it makes the codebase more difficult to maintain. I think the fact Python will soon be an optional language will be a solution for developers who really can't live without the more advanced syntax sugar.

@reduz
Copy link
Member

reduz commented May 8, 2018

I miss functional things a little, especially lambdas which were proposed 3 times now but for some reason never went merged... maybe not enough time to review / big updates were coming at the same time?

I have nothing against them, but implementation needs to be better, and we knew for a long time optional typing was going to be added. I guess when @vnen is done with optional typing, we can take a look at it again.

@mhilbrunner
Copy link
Member

I think it should track python as closely as possible.

Disagree, Python is a different language with way different goals.
Take the ideas that fit and sure, let GDScript be inspired by it, but don't cling to it too hard.

But yeah, lambdas/first class functions and variable amount of arguments for functions (variadic functions) are the two biggest things I miss, Zylann. :P Sorry, thats kind of "feature wishlist" territory again.

Again: Happy birthday, @reduz :)

@spaghiajoeojo
Copy link

This is great in theory, but in practice it makes the codebase more difficult to maintain. I think the fact Python will soon be an optional language will be a solution for developers who really can't live without the more advanced syntax sugar.

Exactly what I meant.
Also.. Happy birthday :)

@nobuyukinyuu
Copy link
Contributor

But they are different things. First is for built-ins, second is for classes, third is not really even a constructor. I feel if they were all the same, it would be more confusing.

Thank you for helping me understand the distinctions and why they are important. Rolling new() into the built-in's special overloaded type-name syntax (but for class names) seems like it wouldn't be inconsistent to the point of creating confusion if the third things (for example when creating a Texture or Image), had overloads of new() that alias to them. In this case, though, it is maybe more syntactical sugar than part of the design goal to simplify the language.

I'd argue against .new() having the same syntax as built-ins being more confusing, however. Users understand TypeName() or TypeName(some arg) to return a new value of that type; they are listed at the top of the public methods in the docs of their respective type, as distinct. Classes don't have this distinction in the docs, probably because I guess only .new() and .instance() exist consistently for them without overload support.

I'm glad that optional typing is on the roadmap, honestly. Optional typing is an important step to providing overload support, which would probably be necessary to make everything I'm saying make sense. My first instinct when starting GDScript a month ago was to "hint" to the script editor on uninitialized vars by using the same syntax used to return a new value type so I could get better autocomplete. Specifically, ClassName() without arguments. Obviously, this didn't work most of the time, and the times when it did probably shouldn't have based on how the language is right now. Anything that can hint autocomplete to understand a var's type better will be helpful to people still learning the language.

(apologies for the long-windedness!)

@mwerezak
Copy link

mwerezak commented May 8, 2018

Keep one Godot way to do things, like there is a Pythonic way to do things.

Reminds me of something I was working with yesterday. When we want the size of an Array, do I use array.size() or len(array)? When I want to append to the end of an array, do I array.append(item) or array.push_back(item)?

Personally I prefer using array.size(). It's more consistent, just a method call like any other. While len(array) seems to be some kind of special language construct that AFAICT exists only to mimic Python.

And similarly with array.push_back(item). There's already a push_front() method so it seems more consistent.

Of course these are very specific examples, but I just wanted to highlight something worth considering.

Also:

More functional vs. more procedural approach.

I don't think this should be an either/or question (not that I think that's what you're implying). Certain things are better handled with a functional approach, other things are better handled with a procedural approach. The two complement each other.

@vnen
Copy link
Member Author

vnen commented May 10, 2018

Thanks for all the responses.

First, let me clarify a few points that seem to stir confusion:

There are 3 different syntax for constructors.

Not really. One are for primitive types, which are not "instances" per se (like Color()). One you create a new instance of an object (Object.new()). The other one is not a constructor, it is meant to get the scene out of a PackedScene resource ( scene.instance(), which is just a regular method from PackedScene).

Array.append() and Array.push_back() do the same thing.

This is more about how you can use the array as queue, list, or stack using more conventional function names. But this should be another discussion since they are not GDScript constructs, they are part of the Godot API. There's an important difference there, because changing this API requires no change to GDScript itself.

The choices aren't mutually exclusive.

My list was more about examples for discussion than strict decisions to make. Many of those can be viewed as a spectrum rather than a discrete boolean choice.

I miss X from my experience with other languages.

That's quite a complicated topic and it relates to my last bullet from the OP. The thing is that while you might want to do it in the way you're used to, there might be another (more Godot-y) way to do the same thing. Hence my question: should we embrace many paradigms or solve this by documenting the translation between those and the Godot way? For instance, signals are very important in Godot but many people seem to forget they exist.

About the len() function.

This is a GDScript function but it serves more to generalize getting the length of different types of collections (for instance, Array.size() vs. String.length()). Though this might only be solving a deficiency in the API itself, which can be discussed. This might apply to a few other built-in function as well.


From what I can gather from this discussion (and the previous history) so far:

  • People are not so fond of syntax sugar, but a few constructs are really wanted.
  • Preference seems to be a small but expressive language.
  • Less ways to do the same stuff are better and that way should be simple.
  • Add stuff to the main Godot API instead of a new constructs/functions in GDScript. I just want to note that adding to the API has its own criteria.
  • There are still sorely missed features in GDScript that make it not expressive enough (variadic functions/splat operator, first-class functions/lambdas, null coalescing operator, and maybe a few others).

Also, while I do like embracing the functional paradigm, if we add first-class functions we'll probably need more functional stuff as well (such as map() and reduce() for collections, currying/partial application). And that will affect that typing system that's on the way (type hints might include not only that it is a "function" but the signature as well). This is something to consider.

@triplefox
Copy link

I have a recommendation and that is to look for some metric of improvement(e.g. ratio of LOC added in GdScript to LOC saved in example scripts). This is a technique I have read that Niklaus Wirth used in his languages, with the main metric in his case being the speed with which the compiler could compile itself. This led to tradeoffs between source code size and algorithmic optimizations, with simpler, cleaner algorithms often winning out when put to the test.

Doing this will help in rejecting bloat going forward, while still allowing for some evolution. Features that eliminate whole classes of errors in user code would override a rule like this(maybe there is a way to capture potential error rate in a number?)

@mhilbrunner
Copy link
Member

IMHO LOC is a bad metric. It only measures volume, not quality. It doesn't capture whether the example code improves, or becomes more readable and maintainable.

Shorter code isn't necessarily better. See arguments about better variables names.

@vnen
Copy link
Member Author

vnen commented May 27, 2018

I assume LOC was only an example of metric, not an actual proposal. The idea of a metric is interesting, but I can't think of anything that would fit nicely into "the Godot way" of doing things.

@vnen vnen mentioned this issue Jun 19, 2018
@razcore-rad
Copy link
Contributor

razcore-rad commented Jun 22, 2018

This is probably controversial, but in my opinion I think that moving towards reactive (not necessarily functional, although it kinda' comes with the territory) is what needs to happen. For multiple reasons, but most of all it's just natural to work with when dealing with time and multimedia interactions. It's the response to contemporary needs and most languages are moving in this direction, mostly through libraries, but there are a few that have built-in functionality (like dart). For generic info on reactive programming and why it's so useful check this.

This imperative type of programming is suited very well for input -> crunch -> output type of applications, not interactive stuff.

Think of the mouse, instead of getting one value at a time (through connecting to the event) and being forced to keep track of the time yourself and do maths with time, control flow etc. is very unnatural. Instead, with composable streams where you have "atomic" functions that operate on incoming data as data is fed into the pipe is far more natural, it's exactly what you'd expect to do, instead of keeping track of each individual event and having to think about all the interactions with future/past events, keeping track of them individually, a stream that takes say for example the events in the last 0.5 sec and gives them to you to process or combine multiple streams or just give you double taps as an event are much more natural.

So I do agree with everyone before me about keeping it simple and maintainable, I just don't think that sticking with the imperative style is suited for interactive applications. It can be done sure but with a lot of boilerplate and it's very easy to get wrong the order in which you process stuff as things get passed around all over the place randomly really, if you think about the user interaction with the game.

edit
I went on and started looking at reactive a bit more and along the way I came across this idea: https://github.com/staltz/callbag-basics, it's more of a meta thing but I think it's rather useful to dissect for further idea generation and different perspectives.

@voithos
Copy link
Contributor

voithos commented Oct 17, 2018

There has been a lot of discussion so far, and from what I gather it seems that we've been able to narrow down on some design goals for GDScript. Is there more to discuss, or is this ready to be concretized in the form of some docs in the roadmap repo, as originally mentioned?

For what it's worth, I agree with a lot of what has already been mentioned, including the summary in #18698 (comment). I would suggest, though, adding "consistency" (both in APIs as well as in GDScript itself) as a goal in addition to the ones you already listed.

Rationale:

Consistency in syntax and naming decreases cognitive load and allows developers to build more of an intuition around how the system works. In some cases, it may be justifiable to break consistency in order to (for example) highlight a truly unique case or make slow / "dangerous" operations easier to detect - but in the general case, I think we should strive for consistency.

For example, as mentioned, the 3 different syntaxes for "constructors" apply to different cases / types of entities, but we should consider whether the differences are important enough to justify different syntax. As a developer using Godot, do I really need to care that Color is primitive and Object isn't, such that I have to do Color() vs Object.new()? If there is a good reason for the developer to care, then perhaps the different syntax is justified - but otherwise, they would ideally be made more consistent.

@Eoin-ONeill-Yokai
Copy link
Contributor

In terms of future elements that godot could have, a short hand for funcref() might be a useful way to provide some kind of functional support without having to support lambdas immediately. I'm thinking of something similar to the way that $ works, maybe using an -> syntax. For instance:

self->gotoscene

Would basically be the equivalent of writing something like....

funcref( self, "gotoscene" )

Idk how others would feel about this, but it could be kind of useful...

@Zylann
Copy link
Contributor

Zylann commented Nov 29, 2018

I don't think it's worth adding a special operator just to shorten funcref, the latter is already quite short to write. I'd prefer actual lambda support if new syntax is involved. At best, for a shortcut, the syntax object.method without () could be a better step towards first-class functions.

@aaronfranke
Copy link
Member

What do the discussions in this thread mean for issues such as #23101? Would such complex things ever be added if they are only useful in some situations for some users?

@Zylann
Copy link
Contributor

Zylann commented Apr 10, 2020

@aaronfranke this comment is quite relevant regarding typed OO GDScript #23101 (comment)
Besides, when you extend nodes you may not have a choice. Just imagine C# without interfaces.

@vnen
Copy link
Member Author

vnen commented May 14, 2020

Adding this to the OP as well for visibility, but these are my current ideas:

  1. GDScript should be expressive but not bloated.
  • E.g. we probably don’t need a list comprehension syntax, but there should be an easy way to do it, in particular for making nested Arrays.
  • In fact, this relates directly to the Godot API. The more things available in the API, the less GDScript needs to do (also, it’ll be available for other languages as well).
  1. GDScript should try to be object-oriented, while allowing some functional paradigms.
  • This means that inheritance, polymorphism, encapsulation principles should be applied.
  • The language should provide first-class functions and implementation of common higher-order functions (such as map, filter, and reduce).
  • Re-use of functionality is also expected. So it needs constructs outside of inheritance to share code (such as Traits).
  1. GDScript should be simple to write, hard to do wrong.
  • Type specifiers are optional, but types should be strong.
  • The compiler must try to catch as many potential errors as possible, showing error and warning messages (in debug mode) for possible programming mistakes.
  • The compiler should make code optimizations based on inferred semantics, even when the programmer is being verbose. E.g. treating a local variable as constant if it’s never changed.
  1. GDScript implementation should be simple.
  • So more people can understand and contribute. We don’t really need a super optimized language, though we do want to have optimizations.
  • Syntax should be simple and fit an LL(k) grammar with a small ‘k’ value. I'm managing to assume LL(1) for the new parser, and so far it works. So I'm avoiding any syntax that increases lookahead (as this makes things harder to parse).
  • Reduce the number of builtin functions to a minimum (use the engine API for more things).

@aaronfranke
Copy link
Member

@vnen E.g. treating a local variable as constant if it’s never changed.

This reminds me, it would be really nice to have local const fields. They would be forcefully unchangeable like class-level constants are, but limited to the scope they're declared in.

I agree with all your points, but I'm a bit confused as to what "LL(k)" means. Does LL(1) mean that any given token's behavior is not changed by the following token? That sounds good to me.

@NathanLovato
Copy link
Contributor

I second @aaronfranke on local constants. In some longer files we've worked on lately, we had quite a few cases where we needed just that (specific tween-based animations with different durations and delays, among other things)

@Calinou
Copy link
Member

Calinou commented May 15, 2020

@aaronfranke There's a proposal about immutable variables here: godotengine/godot-proposals#820

@vnen
Copy link
Member Author

vnen commented May 20, 2020

@aaronfranke

This reminds me, it would be really nice to have local const fields. They would be forcefully unchangeable like class-level constants are, but limited to the scope they're declared in.

I'm already doing this in the GDScript rewrite.

[...] I'm a bit confused as to what "LL(k)" means. Does LL(1) mean that any given token's behavior is not changed by the following token? That sounds good to me.

That's indeed a quite technical term. One L means that it parses left-to-right, the other means "lookahead", and k is the amount of tokens of lookahead need to decide the grammar production (e.g. whether it's a variable or function declaration or something else). LL(1) means the parser needs to look at the next 1 token to decide which function to run. A larger number means it has to buffer more tokens and have more possibilities to decide the next function.

Note: It is possible to have an LL(0) language, which is super simple to parse, but it's very difficult to make it look natural and useful.

@hilfazer
Copy link
Contributor

hilfazer commented Jun 13, 2020

Be newbie friendly.

It already is and should stay that way while becoming even friendlier.
So for example i'd like it to avoid confusing syntax like { = }

var s = "foo"
var d = { s = 5 }
var f = { s : 5 }

Newbie can be unaware of optionally skippable quotes. Or s/he could not notice there's = and not : (or vice versa).

Or here - newbie may think $ is fetching a node named bar:

	var foo = "bar"
	$foo

I'm no expert but i think removing optionally skippable parts of a syntax would make the implementation smaller and thus simpler.

@xix-xeaon
Copy link

I'd like to point out an other area of focus that is rarely actually talked about which is productivity. Having a hammer which is easy to learn how to use, or easy to construct/repair for the manufacturer is all well and good, but the hammer exists to be used to achieve some result. A hammer which is more difficult to learn, or harder to make, is absolutely worth it if it produces better results.

The point of scripting languages is not really to be easy to learn but instead to be easy to work with in order to get high productivity. (This is also what makes them easy to learn.) You want to be able to quickly write only a small amount of code and get your idea working. Compiled languages instead focus on runtime performance.

Python is the epitome of productive languages and GDScript is inspired by it because it shares the same strong focus on productivity, with the desire to be able to quickly write game logic and mechanics. It is important to remember this when considering what features to add.

It's true that maintaining all of the features of Python in GDScript would certainly be too much work, and some things wouldn't even fit at all because of performance since game code often needs to run every single frame. It is, however, not true that Python is difficult to learn - despite all those features. This is because you don't have to use features you don't know.

With this in mind
I certainly agree on having a large standard library, since it increases productivity. Also, as has been pointed out, it doesn't make the language itself more complex so it doesn't add much to maintenance. The requirement for inclusion should be very low - even if it only helps a few only a little.

For instance, Array.extend and Dictionary.update. Making those functions yourself is only 4 lines of code each and most people don't need them most of the time. But including them costs nothing and it increases productivity. I'm guessing there'd also be quite a difference in performance. An other example is a lack of a simple builtin function for listing a directory. Python is full of functions like these and GDScript should be too.

On the subject of list comprehension (with dictionaries too); I know some people really don't like them, and that's fine - you don't have to use them - but they're very productive if you're comfortable with them. Also, generators/iterators from yield in functions and array-slicing syntax. And sets. And itertools etc. Often you're only working with individual things but as soon as you have a bunch of something all of these functions and features become very useful. It's true that they can be hard to wrap your head around for beginners, and I don't know how much those features would affect the parser, but they're just so productivity once you learn them.

Keyword arguments (optional unordered arguments with defaults) is an other example of an extremely productive feature, even for beginners. It almost doesn't matter what the implementation cost is, because it makes it so easy to create objects and call functions with only the arguments you want. With it you can have all the .new() of nodes etc accept the three parameters you want to change right away instead of having them on three extra lines afterwards.

Then there are things like custom datatypes (for overloading operators). Beginners won't use them, that's fine. But in some situations it makes all the other code so much easier to work with, greatly increasing productivity. This feature has performance implications for other code, even when not used, but I think it can be implemented separated from the normal object.

Again these are just examples, but I think productivity should absolutely be a core part of the design goals. GDScript exists to get more work done faster.

@vnen
Copy link
Member Author

vnen commented Jul 25, 2020

Newbie can be unaware of optionally skippable quotes. Or s/he could not notice there's = and not : (or vice versa).

Honestly a newbie will be unaware of many things, because they are a newbie. Such person should be reading documentation and tutorials to learn. The GDScript basics page is not that long (sure, it has a lot to soak in, but such mistakes can be avoided and understood by reading the relevant section again). That's not an excuse to bloat GDScript but we should not strip it and remove features from the pros to write optimal code just because a newbie might stumble on it.

BTW the { a = 1 } syntax is based on the Lua tables, which is supposed to be a basic language. It helps writing dictionaries when every key is a string.

I certainly agree on having a large standard library, since it increases productivity. Also, as has been pointed out, it doesn't make the language itself more complex so it doesn't add much to maintenance. The requirement for inclusion should be very low - even if it only helps a few only a little.

Note that adding things to Godot should follow the proposal workflow. And if something is not used often it won't make the cut. The point is that those things do not need to be provided out of the box from Godot but instead provided as plugins that you can download from the Asset Library. So only those who need it will get it.

For instance, Array.extend and Dictionary.update. Making those functions yourself is only 4 lines of code each and most people don't need them most of the time. But including them costs nothing and it increases productivity. I'm guessing there'd also be quite a difference in performance. An other example is a lack of a simple builtin function for listing a directory. Python is full of functions like these and GDScript should be too.

Well, it costs almost nothing to add one thing. But then one by one this becomes a lot of things, and then it does make a difference. How often you need to list a directory in a game project? If you need to process the entries, iterating a Directory object won't be much different than iterating a list you got with a builtin function. Also is it recursive or not? Should it show hidden (dot) files? As you start adding arguments to customize that, the function isn't so simple anymore (and if you don't add those then it's less useful thus less used). It's not that simple to add stuff without thinking of use cases, that's why we introduced the proposals.

Keyword arguments (optional unordered arguments with defaults) is an other example of an extremtely productive feature, even for beginners. It almost doesn't matter what the implementation cost is, because it makes it so easy to create objects and call functions with only the arguments you want.

Adding keyword arguments would make the release binary bigger and it would introduce a performance cost every time you use them. You know what's gonna happen? It will get a bad rap for the performance penalty and people will start recommending not using it, which renders the feature pointless.

Again these are just examples, but I think productivity should absolutely be a core part of the design goals. GDScript exists to get more work done faster.

My point is that GDScript should be easy to write, which kind of implies that we want people to be productive with it. But it should not happen in detriment of other points. Even though performance is not the goal above it all, we should not make it slow for an edge-case feature.

And again: everybody can open a proposal for a particular feature they need. My ideal for this "design goals" is to be one parameter for the GDScript-related proposals to be considered (notice that I opened this issue way before the proposal system was in place). It's mostly guidelines that are a tad subjective. So most of the examples could actually be added, as long as they go through the workflow we set up. This is not a dismissal for the examples, just an explanation that it's not that simple to "just add everything that is marginally helpful" as that could have an impact in other areas even for people who don't use it.

@Calinou
Copy link
Member

Calinou commented Aug 31, 2021

Closing, as GDScript 2.0 is now feature-complete. Further changes should be discussed on the Godot proposals repository.

@Calinou Calinou closed this as completed Aug 31, 2021
@akien-mga akien-mga added this to the 4.0 milestone Sep 15, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests