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

Add a way to avoid creating callbacks when connecting signals #1990

Closed
me2beats opened this issue Dec 15, 2020 · 9 comments
Closed

Add a way to avoid creating callbacks when connecting signals #1990

me2beats opened this issue Dec 15, 2020 · 9 comments

Comments

@me2beats
Copy link

Describe the project you are working on

Godot plugins

Describe the problem or limitation you are having in your project

Gdscript signal system is powerful and flexible.
But it seems the more connections I have the less readable my code is.
I also faced this problem in other frameworks that use similar pattern.

I think this is mainly because of:

  • when using connect(), I also create a callback method and put there what I want to do when the signal fired. But that means the code becomes less straightforward = less readable.
    So I need to jump to on_ methods every time to know what I do there.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

A way to write more straightforward code without the need to create callback methods (at least explicitly) when connecting signals would solve the problem.

One way could be adding when keyword that could work this way:

func _ready():
    when pressed:
        print('button pressed')

That means each time you press the button, 'button pressed' is printed.

Then you can continue to write the code as usual

func _ready():
    when pressed:
        print('button pressed')

    print('hi')

In this case 'hi' would be printed, without waiting for the button press, unlike yield works for example.

As you can see there's no need for creating callbacks like on_pressed(). The code becomes more readable and straightforward.

Another example:

func _ready():
    when pressed:
        print('button pressed')
    when pressed:
        print('button pressed')

This would print 'button pressed' twice every time button is pressed.
This ability to write multiple when blocks would be very useful to follow divide-and-conquer algorithm and have small separated code blocks.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

  • Each when statement could implicitly create a unique function and connect a signal ('pressed' in the example above) to it.
    Or this could be created as a method of object where when statement is created.

  • In order to be able to disconnect this implicitly created function, the statement could return it for example this way:

func _ready():
    var f = when pressed:
        print('button pressed')

Where f could be the callback name

I think there's no need to return Error code (that connect() does) because the callback function with connected signal should be created anyway.

  • in case the signal belongs to some other object (say named other_node, this syntax could be used:
func _ready():
    when other_node, pressed:
        print('button pressed')
  • Signals can have arguments, so it could be look like:
func _ready():
    when other_node, pressed, args_array:
        print('args')

This prints pressed signal args array (which are passed in emit_signal) if any.

Not sure if it should have connect flags like connect() has.

The downsides I can see:

  • a new keyword created (when)
  • this keyword may be confusing for people that used to other meaning of when keyword (c#, nim etc)
  • some limitations should be introduced I think, like prohibition to write multilevel when statements
  • not sure how easy it would be to implement this.

If this enhancement will not be used often, can it be worked around with a few lines of script?

not possible now.

Is there a reason why this should be core and not an add-on in the asset library?

Gdscript syntax feature that may hopefully be used often.

@omicron321
Copy link

first, one suggestion: maybe reuse same keyword for same feature
second, maybe introducing lambda functions would help to unclutter code from additionnal function naming and from jumping to further code blocs.

@Jummit
Copy link

Jummit commented Dec 16, 2020

create a callback method and put there what I want to do when the signal fired. But that means the code becomes less straightforward = less readable.

I would say the opposite: putting callbacks into a function makes it more readable than the proposed inline solution.

In my oppinion, your problem (So I need to jump to on_ methods every time to know what I do there.) doesn't justify a new keyword + syntactic sugar.

@bluenote10
Copy link

Isn't the bigger problem that this would require full closure support? For instance:

func _ready():
    var foo = ...
    var f = when pressed:
        print(foo)

Where should foo come from in the callback? Is it closed over by value or by reference?

I vaguely remember that there were some previous discussions about introducing lambdas.

@Host32
Copy link

Host32 commented Dec 16, 2020

I dont think that this proposal it's the best way to resolve the problem of legibility, its sounds like just put the connection code in another place with a litte better code organization, but its not solve the real problem of complex architetures.

I solved this question in my projects using intermediate proxies to centralize the connections and show me the dependencies in a more efficiente way:

image

I thintk that the rastreability problem of a lot of connections could be solved more efficiently with a better arquiteture and separation of resposabilities in your especific project.

@me2beats
Copy link
Author

first, one suggestion: maybe reuse same keyword for same feature
@Omicron666 What do you mean by that?

@me2beats
Copy link
Author

@Host32
Your approach can be useful for reusing components and dynamically connecting / disconnecting them, but as far as I know, such a system (at least written in gdscript and using the built-in node system) has certain inconveniences and limitations.
In my practice, it often happens that such components are quite specific and often they are not planned to be reused.
Instead, I want to write simple, straightforward code without having to build such systems (KISS principle).

@me2beats
Copy link
Author

I would say the opposite: putting callbacks into a function makes it more readable than the proposed inline solution.

In my oppinion, your problem (So I need to jump to on_ methods every time to know what I do there.) doesn't justify a new keyword + syntactic sugar.

@Jummit Yes, I really think that frequent jumping from one method / class to another worsens the readability of the code, especially when the code base grows.

For example imo using yield looks clearer than connect(.., CONNECT_ONESHOT) + creating a callback method and putting there all required arguments

@me2beats
Copy link
Author

me2beats commented Dec 16, 2020

@bluenote10

func _ready():
    print('hello')
    var foo = 1
    var f = when pressed:
        print(foo)
    print('world')
  1. First of all, 'hi' is printed

  2. Then foo declared and defined.

  3. Unique method _on_pressed() created <- maybe the problem is here (in dinamic method creation)?

  4. signal pressed is connected to _on_pressed().

  5. world is printed

  6. each time the button is pressed, "1" is printed

this is how I see it.
Does this require closures?

@me2beats
Copy link
Author

me2beats commented Mar 8, 2021

Closing in favor of lambdas #2431

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

7 participants