godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.12k stars 69 forks source link

Add `Callable.bind_reverse()` to later read arguments from left to right instead of right to left #10533

Open kevinloustau opened 3 weeks ago

kevinloustau commented 3 weeks ago

Describe the project you are working on

A co-op game online.

Describe the problem or limitation you are having in your project

When we use a Callable.bind() method, later the arguments of the function are read by default from right to left.

In the example below, I needed to write a wrapper function to reverse the args:

# function to swap arguments order to use .bind()
func change_volume_bus(volume, bus_idx):
    AudioServer.set_bus_volume_db(bus_idx, volume)

func mute_bus_fade(bus_idx):
    var t = create_tween()
    var bus_vol = change_volume_bus.bind(bus_idx)
    t.tween_method(bus_vol, AudioServer.get_bus_volume_db(bus_idx), -80.0, 1)

func demute_bus_fade(bus_idx):
    var t = create_tween()
    var bus_vol = change_volume_bus.bind(bus_idx)
    t.tween_method(bus_vol, AudioServer.get_bus_volume_db(bus_idx), 0.0, 1)

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

Have another bind() method that can read the arguments from left to right.

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

With the example above and the bind_reverse() that read the arguments from left to right:

func mute_bus_fade(bus_idx):
    var t = create_tween()
    var bus_vol = change_volume_bus.bind_reserve(bus_idx)
    t.tween_method(bus_vol, AudioServer.get_bus_volume_db(bus_idx), -80.0, 1)

func demute_bus_fade(bus_idx):
    var t = create_tween()
    var bus_vol = change_volume_bus.bind_reserve(bus_idx)
    t.tween_method(bus_vol, AudioServer.get_bus_volume_db(bus_idx), 0.0, 1)

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

Yes, it can be done by doing a wrapper functions reversing the order of the arguments

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

Callable.bind() is a core function.

Calinou commented 3 weeks ago

I'm not sure if it's worth adding a second method that essentially achieves the same result, unless it allows doing things that were previously impossible or it has performance advantages in some scenarios. (These are the main reasons why methods like String.rsplit() exist.)

Having only one order ensures you never get it wrong, regardless of which method you were using to bind arguments.

KoBeWi commented 3 weeks ago

It's the same as #4920, no? Also #6185, for the given example.

passivestar commented 3 weeks ago

I think in your example a lambda is an easy solution?

func mute_bus_fade(bus_idx):
    var t = create_tween()
    t.tween_method(
        func(val): AudioServer.set_bus_volume_db(bus_idx, val),
        AudioServer.get_bus_volume_db(bus_idx), -80.0, 1
    )

Being able to bind from the left is nice, but it wouldn't bring much value over lambdas on its own without other functional tools to take full advantage of it (i.e function composition). Personally I would love to have a good functional library in gdscript, possibly something like this (which is addon territory). However GDScript currently lacks variadic functions to implement such a library. With them you would be able to implement any higher order function you want yourself, including left bind