Nested Type Annotations #127

Closed
opened 2025-12-30 01:21:16 +01:00 by adam · 9 comments
Owner

Originally created by @yesteryearer on GitHub (Apr 2, 2024).

Good day,

I'm pretty new to Pkl, and despite perusing the docs, can't find a working solution for the following:

I'm trying to generate a template for an object of the following form:

table String {
    mapping String {
            gp_table: String
            gp_field: String
            bc_table: String
            bc_field: String
    }
}

In the above scenario, any table instances, must be able to contain any number of mappings.

The solution I designed got as far as, a mapping.pkl file:

module Mapping

class Mapping{
    gp_table: String
    gp_field: String
    bc_table: String
    bc_field: String
}

And an instance as follows:

import "mappings.pkl"

salesInvoices {
    documentNo_to_invoiceNumber: Mapping = new {
        ["gp_table"] = "RM20101"
        ["gp_field"] = "DOCNUMBR"
        ["bc_table"] = "salesInvoices"
        ["bc_field"] = "externalDocumentNumber"
    }
}

The above only works when the mapping isn't contained within the larger salesInvoices object, and either way, I will only be able to have a single mapping in this manner.

Is there a clever work around that I'm missing? Or rather, what is the best way to go about this? I saw the following, but in this instance I think the object only required a single instance of the nested object.

Originally created by @yesteryearer on GitHub (Apr 2, 2024). Good day, I'm pretty new to Pkl, and despite perusing the docs, can't find a working solution for the following: I'm trying to generate a template for an object of the following form: ``` table String { mapping String { gp_table: String gp_field: String bc_table: String bc_field: String } } ``` In the above scenario, any `table` instances, must be able to contain any number of `mappings`. The solution I designed got as far as, a `mapping.pkl` file: ``` module Mapping class Mapping{ gp_table: String gp_field: String bc_table: String bc_field: String } ``` And an instance as follows: ``` import "mappings.pkl" salesInvoices { documentNo_to_invoiceNumber: Mapping = new { ["gp_table"] = "RM20101" ["gp_field"] = "DOCNUMBR" ["bc_table"] = "salesInvoices" ["bc_field"] = "externalDocumentNumber" } } ``` The above only works when the mapping isn't contained within the larger `salesInvoices` object, and either way, I will only be able to have a single mapping in this manner. Is there a clever work around that I'm missing? Or rather, what is the best way to go about this? I saw the [following](https://github.com/apple/pkl/issues/331), but in this instance I think the object only required a single instance of the nested object.
adam closed this issue 2025-12-30 01:21:16 +01:00
Author
Owner

@yesteryearer commented on GitHub (Apr 2, 2024):

Even if there was a solution as the original poster said here, but with an undefined number of internal objects of the declared type, that would be wonderful. Not looking for an elegant solution.

@yesteryearer commented on GitHub (Apr 2, 2024): Even if there was a solution as the original poster said [here](https://github.com/apple/pkl/issues/352), but with an undefined number of internal objects of the declared type, that would be wonderful. Not looking for an elegant solution.
Author
Owner

@holzensp commented on GitHub (Apr 2, 2024):

I'm not too sure what

table String {
    mapping String {
            gp_table: String
            gp_field: String
            bc_table: String
            bc_field: String
    }
}

means. If you know the field names ahead of time, you don't want a Mapping, but rather a class that defines properties. Only when you don't know the names ahead of time do you tend to use a Mapping.

Be weary that import "mappings.pkl" doesn't actually do anything (you don't use mappings anywhere); at the moment, Pkl only checks that salesInvoices.documentNo_to_invoiceNumber is indeed a Mapping, but nothing else.

@holzensp commented on GitHub (Apr 2, 2024): I'm not too sure what ``` table String { mapping String { gp_table: String gp_field: String bc_table: String bc_field: String } } ``` means. If you know the field names ahead of time, you don't want a `Mapping`, but rather a `class` that defines _properties_. Only when you don't know the names ahead of time do you tend to use a `Mapping`. Be weary that `import "mappings.pkl"` doesn't actually do anything (you don't use `mappings` anywhere); at the moment, Pkl only checks that `salesInvoices.documentNo_to_invoiceNumber` is indeed a `Mapping`, but nothing else.
Author
Owner

@yesteryearer commented on GitHub (Apr 2, 2024):

Mapping in this case is not as per the Pkl definition, but rather a mapping between two databases. Hence the different tables and fields.

The first code block was supposed to just illustrate the general form of the object I'm trying to create.

Does the second code block not define a class with specific properties?

And finally, the final code doesn't even run, it throws an error saying: annotated types can't be applied to non-local objects or something along those lines.

All I'm trying to do is enforce a specific form on to the objects of type 'table', where any one table can have one or many objects contained within it of the form:

class Mapping{
    gp_table: String
    gp_field: String
    bc_table: String
    bc_field: String
}

Where each gp_table, gp_field, etc, variables will have varying values attributing to them.

@yesteryearer commented on GitHub (Apr 2, 2024): Mapping in this case is not as per the Pkl definition, but rather a mapping between two databases. Hence the different tables and fields. The first code block was supposed to just illustrate the general form of the object I'm trying to create. Does the second code block not define a class with specific properties? And finally, the final code doesn't even run, it throws an error saying: `annotated types can't be applied to non-local objects` or something along those lines. All I'm trying to do is enforce a specific form on to the objects of type 'table', where any one table can have one or many objects contained within it of the form: ``` class Mapping{ gp_table: String gp_field: String bc_table: String bc_field: String } ``` Where each gp_table, gp_field, etc, variables will have varying values attributing to them.
Author
Owner

@odenix commented on GitHub (Apr 2, 2024):

I guess you want something like the following:

mappings.pkl:

tables: Mapping<String, DbTable>

typealias DbTable = Mapping<String, DbMapping>

class DbMapping {
  gp_table: String
  gp_field: String
  bc_table: String
  bc_field: String
}
amends "mappings.pkl"

tables {
  ["salesInvoices"] {
    ["documentNo_to_invoiceNumber"] {
      gp_table = "RM20101"
      gp_field = "DOCNUMBR"
      bc_table = "salesInvoices"
      bc_field = "externalDocumentNumber"
    }
  }
}

PS: This is a better fit for "Discussions" than "Issues".

@odenix commented on GitHub (Apr 2, 2024): I guess you want something like the following: mappings.pkl: ``` tables: Mapping<String, DbTable> typealias DbTable = Mapping<String, DbMapping> class DbMapping { gp_table: String gp_field: String bc_table: String bc_field: String } ``` ``` amends "mappings.pkl" tables { ["salesInvoices"] { ["documentNo_to_invoiceNumber"] { gp_table = "RM20101" gp_field = "DOCNUMBR" bc_table = "salesInvoices" bc_field = "externalDocumentNumber" } } } ``` PS: This is a better fit for "Discussions" than "Issues".
Author
Owner

@yesteryearer commented on GitHub (Apr 3, 2024):

@translatenix

Thank you so much, this is excellent + exactly what I was looking for.

I understand the code off the bat, but if you are willing to give a better explanation of your thought process, I'm all ears. Does mapping instantiate a many-to-one relationship? The only thing I don't really get is the distinction between using the variable name such as tables and the keyword typealias, but I guess this is due to the fact that there is only a single tables object and many DbTables.

Also, I wasn't even aware of the "Discussions" section, so thanks again. First time I've made a forum post on GitHub.

@yesteryearer commented on GitHub (Apr 3, 2024): @translatenix Thank you so much, this is excellent + exactly what I was looking for. I understand the code off the bat, but if you are willing to give a better explanation of your thought process, I'm all ears. Does `mapping` instantiate a many-to-one relationship? The only thing I don't really get is the distinction between using the variable name such as `tables` and the keyword `typealias`, but I guess this is due to the fact that there is only a single `tables` object and many `DbTable`s. Also, I wasn't even aware of the "Discussions" section, so thanks again. First time I've made a forum post on GitHub.
Author
Owner

@odenix commented on GitHub (Apr 3, 2024):

The type alias only improves readability and reusability.
Otherwise it's the same as tables: Mapping<String, Mapping<String, DbMapping>>.
A Pkl Mapping is often called "map" or "dictionary" in other programming languages.

@odenix commented on GitHub (Apr 3, 2024): The type alias only improves readability and reusability. Otherwise it's the same as `tables: Mapping<String, Mapping<String, DbMapping>>`. A Pkl `Mapping` is often called "map" or "dictionary" in other programming languages.
Author
Owner

@StefMa commented on GitHub (Apr 3, 2024):

There is also a Map type in Pkl with an richer API 🤓

@StefMa commented on GitHub (Apr 3, 2024): There is also a Map type in Pkl with an richer API 🤓
Author
Owner

@holzensp commented on GitHub (Apr 3, 2024):

Entirely with @translatenix's suggestions (both code and Discussion; but now that it's here, that's alright... do please close this Issue when you're fully satisfied, @yesteryearer).

Since you're talking about database tables and fields, I bet there are restrictions on the names of those things. It depends entirely on which database you're using what those restrictions are, but just for the example, I'm assuming names may not contain whitespace. This is to demonstrate type constraints and to further illustrate @translatenix's point about typealiases:

tables: Mapping<String, DbTable>

typealias DbTable = Mapping<String, DbMapping>

/// The database can't take arbitrary strings as names, so names must be restricted.
typealias DbName = String(!contains(" "))

class DbMapping {
  gp_table: DbName
  gp_field: DbName
  bc_table: DbName
  bc_field: DbName
}
@holzensp commented on GitHub (Apr 3, 2024): Entirely with @translatenix's suggestions (both code and Discussion; but now that it's here, that's alright... do please close this Issue when you're fully satisfied, @yesteryearer). Since you're talking about database tables and fields, I bet there are restrictions on the names of those things. It depends entirely on which database you're using what those restrictions are, but just for the example, I'm assuming names may not contain whitespace. This is to demonstrate _type constraints_ and to further illustrate @translatenix's point about `typealias`es: ``` tables: Mapping<String, DbTable> typealias DbTable = Mapping<String, DbMapping> /// The database can't take arbitrary strings as names, so names must be restricted. typealias DbName = String(!contains(" ")) class DbMapping { gp_table: DbName gp_field: DbName bc_table: DbName bc_field: DbName } ```
Author
Owner

@yesteryearer commented on GitHub (Apr 3, 2024):

So to clarify, the reason I originally posted this here was because of the following error:

–– Pkl Error ––
A non-local object property cannot have a type annotation.

At which point I didn't know a workaround exists.

Thanks for the help.

@yesteryearer commented on GitHub (Apr 3, 2024): So to clarify, the reason I originally posted this here was because of the following error: ``` –– Pkl Error –– A non-local object property cannot have a type annotation. ``` At which point I didn't know a workaround exists. Thanks for the help.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/pkl#127