HaxeFoundation / HaxeFoundation/haxe

Embedded/inlined objects.

Open
#10,497 2 comments 4 reactions 0 assignees View on GitHub
Dominant language
Haxe
Stars
6.9k
Forks
715
Avg merge
2d 2h
Merged PRs (30d)
11

Description

Every now and then when profiling stuff I run into bottlenecks caused by composition, which are quite easily avoided by decomposing some object into the composite. And I wonder if we could let the compiler do the job. FWIW [Go has something quite similar](https://eli.thegreenplace.net/2020/embedding-in-go-part-1-structs-in-structs/) (although it's designed in a way that's geared towards compensating for Go's lack of inheritance, so it's not necessarily the best template for Haxe).

Example:

```haxe
class Car {
final horn = new Horn();
final engine = new Engine();
final sensors = new Sensors();
public function attemptToDrive() {
if (sensors .detectObstacle()) horn.honk();
else engine.accelerate();
}
}
```

This allocates 4 objects instead of one and also there are more reference lookups to be done to call the actual methods.

Assuming that the field holding an object is `final`, it would be great if one could instruct the compiler to embed it. For example via `inline final` (a combination currently forbidden), since `final` already demands initialization in the constructor. Also the embedded object would have to be the instance of a final class, or assigning subclasses would have to be disallowed for such fields.

The usage would then have the following effect:

```haxe
class Car {
inline final horn = new Horn();
final engine = new Engine(); // the inlining may also be applied to the other fields if so desired, but we'll skip that for brevity
final sensors = new Sensors();
public function attemptToDrive() {...}
}
// becomes
class Car {
var _hx_horn_field1 = field1FromHornConstructor;
var _hx_horn_field2 = field2FromHornConstructor;
function _hx_horn_honk() implementationOfHornHonkUsingLocalFields;
// ...
public function attemptToDrive() {
if (sensors .detectObstacle()) _hx_horn_honk();
else engine.accelerate();
}
}
```

For those wondering, I would suggest `_hx_horn_honk` needn't be `inline` by default as the user can still use `inline horn.honk()` at the call site to achieve the same.

As important an additional requirement, access to the embedded object cannot be allowed in value places (because the object is not a value at runtime), similarly to what's already the case for inline extern methods. Other than that, the semantics shouldn't be affected.

I must admit this is rare enough to do by hand when the bottleneck presents itself. That said, having to deal with the duplicate code is rather unpleasant. Also, it'd be nice to quickly have the compiler do this in order to quickly find the sweet spot of which objects to embed. Also that sweet spot may vary between targets (since different runtimes exhibit different performance characteristics when dealing with object graphs) and so fencing the `inline` to reflect that is far more easily accomplished than conditionally choosing between a composed and a manually inlined version. Lastly, it would allow users who're more constrained by code size to generate the composed and thus (usually) more compact version.

Contributor guide

Open the contributing guide

Research direction

No files, tests, or compiler entry points are named in the issue. Begin by locating how Haxe handles final fields, inline members, and object-versus-value semantics; done would require an agreed design and implementation covering the stated restrictions.

Written by the indexing model from the issue text.

Assessment

Domain
compilers, performance
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.