Skip to content

Conversation

@Mikulas
Copy link

@Mikulas Mikulas commented Oct 27, 2025

Adds a toMixin() method to the Object class that converts an Object
into a Mixin function applicable via the pipe operator (|>).

A generic toMixin() method cannot be implemented in user land, so this
implementation provides a native method that properly handles:

  • Property merging and overriding
  • Element appending with correct index offsetting
  • Entry merging with proper key handling
  • Nested object replacement vs amendment semantics

Implementation uses the source Object's enclosing frame to ensure
proper module context for type resolution during member evaluation.

edit: updated from Dynamic to Object

Copy link
Contributor

@HT154 HT154 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this not be done in userland Pkl?

function dynamicToMixin(mix: Dynamic): Mixin = new {
  ...mix
}

It's likely that the "deep" variant of this that recursively turns a nested object structure (and not just a Dynamic!) into a Mixin isn't userland-friendly, but I'm not sure there needs to be an API for the shallow implementation.

Missed the implementation detail that makes this work, never mind :)

Comment on lines 204 to 205
// If the key is a Long (element index), offset it by parentLength
if (key instanceof Long) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a sound assumption to make. Mappings and Dynamics can have entries with Int (in Pkl, Long in Java) keys. Probably better to check member.isElement() here instead (and you can then assume the key is a Long).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be the internal field representation, not the userland defined keys. I will see if I can get the key to be the right type at compile time and replace this check

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated to if (member.isElement()) {

@Mikulas
Copy link
Author

Mikulas commented Oct 27, 2025

@HT154 I don't believe so due to how amends vs replacements are constructed. Consider this example:

local base = new {
  a1 {
    b1 = 2
  }
  a2 {
    b1 = 2
  }
}

local over = new Mixin {
  a1 = new Dynamic {
    b2 = 2
  }
  a2 {
    b2 = 2
  }
}

local overrideValue = new Dynamic {} |> over

function dynamicToMixin(mix: Dynamic): Mixin = new {
  ...mix
}

actualValue = base |> dynamicToMixin(overrideValue)

expectedValue = base |> over

Which evaluates to

actualValue {
  a1 {
    b2 = 2
  }
  a2 { // ❗️This is not correct, the original Mixin has amend semantics here, not replace
    b2 = 2
  }
}
expectedValue {
  a1 {
    b2 = 2
  }
  a2 {
    b1 = 2
    b2 = 2
  }
}

Adds a toMixin() method to the Object class that converts an Object
into a Mixin function applicable via the pipe operator (|>).

A generic toMixin() method cannot be implemented in user land, so this
implementation provides a native method that properly handles:
- Property merging and overriding
- Element appending with correct index offsetting
- Entry merging with proper key handling
- Nested object replacement vs amendment semantics

Implementation uses the source Object's enclosing frame to ensure
proper module context for type resolution during member evaluation.
@Mikulas Mikulas changed the title Add Dynamic.toMixin() method Add Object.toMixin() method Nov 10, 2025
@Mikulas
Copy link
Author

Mikulas commented Nov 10, 2025

Updated to implement on Object rather then Dynamic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants