Skip to content

Conversation

@Mikulas
Copy link

@Mikulas Mikulas commented Oct 27, 2025

Adds a toMixin() method to the Dynamic class that converts a Dynamic 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 Dynamic's enclosing frame to ensure proper module context for type resolution during member evaluation.

Adds a toMixin() method to the Dynamic class that converts a Dynamic
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 Dynamic's enclosing frame to ensure
proper module context for type resolution during member evaluation.
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

@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
  }
}

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