Using #lambda magic - YellowAfterlife/GMEdit GitHub Wiki

GMS≥2.3 note: This GMEdit feature has been superseded by native function literal support.

While older versions of GameMaker lack ability to easily organize inline and related scripts, that is a fixable oversight.

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.