Missing union type support for Kotlin codegen #15

Open
opened 2025-12-30 01:19:32 +01:00 by adam · 7 comments
Owner

Originally created by @madisp on GitHub (Feb 3, 2024).

Execution failed for task ':pkl-playground:genKotlin'.
> Pkl union types are not supported by the Kotlin code generator.

Bit of a tall ask, I know, but it'd be wonderful if we could map union types into some form of Kotlin structure.

Originally created by @madisp on GitHub (Feb 3, 2024). ``` Execution failed for task ':pkl-playground:genKotlin'. > Pkl union types are not supported by the Kotlin code generator. ``` --- Bit of a tall ask, I know, but it'd be wonderful if we could map union types into some form of Kotlin structure.
Author
Owner

@bioball commented on GitHub (Feb 4, 2024):

It'd be great to support this, but it's not clear how they should turn into Kotlin. Feel free to provide suggestions, though.

@bioball commented on GitHub (Feb 4, 2024): It'd be great to support this, but it's not clear how they should turn into Kotlin. Feel free to provide suggestions, though.
Author
Owner

@jamesward commented on GitHub (Feb 4, 2024):

For now this will have to be implemented with sealed class https://kotlinlang.org/docs/sealed-classes.html

@jamesward commented on GitHub (Feb 4, 2024): For now this will have to be implemented with `sealed class` https://kotlinlang.org/docs/sealed-classes.html
Author
Owner

@madisp commented on GitHub (Feb 4, 2024):

yup, most likely. sealed class needs a name though, maybe take a similar approach to string union to enum mapping that all union types should be defined via a type alias?

E.g. this wouldn't still map to Kotlin:

class Foo {
  items: Listing<Bar | Baz>
}

but this would?

typealias FooItem = Bar | Baz

class Foo {
  items: Listing<FooItem>
}
@madisp commented on GitHub (Feb 4, 2024): yup, most likely. `sealed class` needs a name though, maybe take a similar approach to string union to enum mapping that all union types should be defined via a type alias? E.g. this wouldn't still map to Kotlin: ``` class Foo { items: Listing<Bar | Baz> } ``` but this would? ``` typealias FooItem = Bar | Baz class Foo { items: Listing<FooItem> } ```
Author
Owner

@giordano-newday commented on GitHub (Jul 1, 2024):

Hi all, I'm evaluating pkl as a configuration language for an API GW. My server is in Kotlin and not having union mapped to Kotlin (or Java) is a show stopper.
There any thoughts about adding them? or what would be a workaround for something with the same semantic but supported by Kotlin?
My scenario is something like typealias HttpMethod = "GET" | "POST" | "PUT" | "DELETE"
Thanks

@giordano-newday commented on GitHub (Jul 1, 2024): Hi all, I'm evaluating pkl as a configuration language for an API GW. My server is in Kotlin and not having union mapped to Kotlin (or Java) is a show stopper. There any thoughts about adding them? or what would be a workaround for something with the same semantic but supported by Kotlin? My scenario is something like `typealias HttpMethod = "GET" | "POST" | "PUT" | "DELETE"` Thanks
Author
Owner

@bioball commented on GitHub (Jul 1, 2024):

@giordano-newday: We don't have a great way to model most union types as Kotlin, because it's missing anonymous sum types. However, typealiases of string literal unions are a special case, and are no problem to represent.

This pkl:

typealias HttpMethod = "GET" | "POST" | "PUT" | "DELETE"

Turns into the following Kotlin:

  enum class HttpMethod(
    val value: String
  ) {
    GET("GET"),

    POST("POST"),

    PUT("PUT"),

    DELETE("DELETE");

    override fun toString() = value
  }
@bioball commented on GitHub (Jul 1, 2024): @giordano-newday: We don't have a great way to model most union types as Kotlin, because it's missing anonymous sum types. However, typealiases of string literal unions are a special case, and are no problem to represent. This pkl: ```pkl typealias HttpMethod = "GET" | "POST" | "PUT" | "DELETE" ``` Turns into the following Kotlin: ```kt enum class HttpMethod( val value: String ) { GET("GET"), POST("POST"), PUT("PUT"), DELETE("DELETE"); override fun toString() = value } ```
Author
Owner

@giordano-newday commented on GitHub (Jul 1, 2024):

I think it would for my scenarios. Thanks @bioball.

@giordano-newday commented on GitHub (Jul 1, 2024): I think it would for my scenarios. Thanks @bioball.
Author
Owner

@devinrsmith commented on GitHub (Jul 18, 2025):

A strategy I've used to implement union types before involves defining an interface with a Visitor callback that has methods for each of the union types. In some domains, the specific items in the union could extend the union type themselves, but in a general purpose system where the same type might be in multiple unions, it's probably best to have an internal wrapper type. Here's a quick example of something that could be considered:

typealias FooItem = Bar | Baz
typealias ZapItem = Bar | Baz | Bap
public interface FooItem {

  <T> T walk(Visitor<T> visitor);

  interface Visitor<T> {
    T visit(Bar bar);

    T visit(Baz baz);
  }
}

public interface ZapItem {

  <T> T walk(Visitor<T> visitor);

  interface Visitor<T> {
    T visit(Bar bar);

    T visit(Baz baz);

    T visit(Bap bap);
  }
}

private static class FooItemBar implements FooItem {
  private final Bar bar;
  // ...

  @Override
  public <T> T walk(FooItem.Visitor<T> visitor) {
    return visitor.visit(bar); // FooItem.Visitor#visit(Bar)
  }
}

private static class FooItemBaz implements FooItem {
  private final Baz baz;
  // ...

  @Override
  public <T> T walk(FooItem.Visitor<T> visitor) {
    return visitor.visit(baz); // FooItem.Visitor#visit(Baz)
  }
}

private static class ZapItemBar implements ZapItem {
  private final Bar bar;
  // ...

  @Override
  public <T> T walk(ZapItem.Visitor<T> visitor) {
    return visitor.visit(bar); // ZapItem.Visitor#visit(Bar)
  }
}

private static class ZapItemBaz implements ZapItem {
  // ...
}

private static class ZapItemBap implements ZapItem {
  // ...
}

Somewhat orthogonal (as in, this could be done alongside the callback pattern), but potentially easier to conceptualize / use:

public interface FooItem {

  enum Type { BAR, BAZ }

  Type getType();

  Bar getBar();

  Baz getBaz();
}
@devinrsmith commented on GitHub (Jul 18, 2025): A strategy I've used to implement union types before involves defining an interface with a Visitor callback that has methods for each of the union types. In some domains, the specific items in the union could extend the union type themselves, but in a general purpose system where the same type might be in multiple unions, it's probably best to have an internal wrapper type. Here's a quick example of something that could be considered: ``` typealias FooItem = Bar | Baz typealias ZapItem = Bar | Baz | Bap ``` ```java public interface FooItem { <T> T walk(Visitor<T> visitor); interface Visitor<T> { T visit(Bar bar); T visit(Baz baz); } } public interface ZapItem { <T> T walk(Visitor<T> visitor); interface Visitor<T> { T visit(Bar bar); T visit(Baz baz); T visit(Bap bap); } } private static class FooItemBar implements FooItem { private final Bar bar; // ... @Override public <T> T walk(FooItem.Visitor<T> visitor) { return visitor.visit(bar); // FooItem.Visitor#visit(Bar) } } private static class FooItemBaz implements FooItem { private final Baz baz; // ... @Override public <T> T walk(FooItem.Visitor<T> visitor) { return visitor.visit(baz); // FooItem.Visitor#visit(Baz) } } private static class ZapItemBar implements ZapItem { private final Bar bar; // ... @Override public <T> T walk(ZapItem.Visitor<T> visitor) { return visitor.visit(bar); // ZapItem.Visitor#visit(Bar) } } private static class ZapItemBaz implements ZapItem { // ... } private static class ZapItemBap implements ZapItem { // ... } ``` Somewhat orthogonal (as in, this could be done alongside the callback pattern), but potentially easier to conceptualize / use: ```java public interface FooItem { enum Type { BAR, BAZ } Type getType(); Bar getBar(); Baz getBaz(); } ```
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/pkl#15