Skip to content

Using #lambda magic

Vadim Dyachenko edited this page Nov 5, 2018 · 3 revisions

While inline/lambda functions are on the roadmap, GML doesn't support them just yet. However, if you've already checked the other wiki pages here, you might know where this is going.

General idea

The premise is simple enough - when you save a file, GMEdit puts your #lambda scripts into an extension. When you open the file, GMEdit extracts them from the extension for ease of editing. Let's suppose you have the following:

#event create
on_click = #lambda { trace("hello!") };

#event mouse_left_press
script_execute(on_click);

If you were then to inspect your event in GMS, it might look like so:

on_click = __lf_obj_test_create_;

while the extension's GML file would have the actual script and a bit of metadata for displaying it as authored:

#define __lf_obj_test_create_
//!#lambda$
 { trace("hello!") }

Setting up

  • Add a new extension called gmedit_lambda

    (right-click Extensions section in resource tree)

  • Add a placeholder GML file to it

Normal use

The shortest syntax for lambdas is just like so:

#lambda { ...code }

you can assign it to a variable,

on_click = #lambda { show_debug_message("hi!"); }

or use it as a function argument,

scr_wait(#lambda {
    show_debug_message("hi!");
}, room_speed * 3);

or even call it immediately if you really just wanted something that you could return from,

var target = #lambda {
    with (obj_player) {
        if (collision_line(x, y, other.x, other.y, obj_wall, false, false) == noone) return self;
    }
    return noone;
} ();

Arguments

While you can use argument or #args magic as usual inside inline functions, you may instead include arguments on the declaration line for even lower redundancy:

#lambda (...args) { ...code }

So, for instance, if you have a script that brings up a custom input prompt and later calls the specified script with the result, you could have it like so:

scr_prompt(#lambda (name) {
    scr_say("Hi, " + name + "!");
}, "What's your name?", "a person");

Various #args features (optional, default values) are also supported.

Names

You can name your inline functions, like so:

#lambda name { ...code }
#lambda name(...args) { ...code }

This does 2 things for you:

  1. Uses name as suffix in auto-generated scripts (for ease of debugging)
  2. Allows to reference/call the function by defined name in the same script/event
var fn = #lambda some {
    show_debug_message("hi!");
};
script_execute(fn); // works
script_execute(some); // also works
some(); // shorter!
on_click = #lambda rec(i) {
    if (i > 0) rec(i - 1);
    show_debug_message(i);
};

Use as a statement

If you do not want to assign the resulting function anywhere, you can do so by replacing #lambda with #lamdef in any syntax version,

/// scr_some(...)
#lamdef debug(v) {
    show_debug_message("scr_some: " + string(v));
}
/// ...
debug("OK!");

Known issues / Limitations

  • Variable/local scope capture is not supported; it is not known what implementation GML itself is going to use.

    In less technical terms, an inline function is still just a script, and will not be able to use surrounding script's local variables unless you feed them to it as an argument.

  • GMEdit does not differentiate between in-lambda/out-of-lambda local variables - they are all highlighted as per containing script/event/moment. This will not change for a while as the choice was between this and having lambdas defined at the end of a file (which is admittedly less fancy). If you feel like you could rewrite scope detection to mind closure nesting while retaining the required performance, you can check out gml.GmlScopes.

You can’t perform that action at this time.