Skip to content

Supporting recursive types #451

@JQuezada0

Description

@JQuezada0

First off, great framework! As someone who hasn't worked a lot with data interchange formats, I was surprised that the first formats I ran into didn't have both strict support for non-nullable types and ADTs.

Description
I'm trying to define a recursive type, but it looks like it's not supported.

[Error] Cycle detected: types.Foo → types.FooOrBar → types.Foo.

Schema:

struct Foo {
    a: String = 0
    b: FooOrBar = 1 
}

struct Bar {
    a: String = 0
}

choice FooOrBar {
    foo: Foo = 0
    bar: Bar = 1
}

I was hoping that making the field optional, or replacing a directly recursive field with a choice that allows the recursion to end would allow the codegen to succeed, but it looks like recursive types in any form will trigger the error.

Are there any thoughts / plans on supporting this? I could imagine having some ability to limit the depth, like Rust's ![recursion_limit = "512"].

This would have to result in a Box on the rust side: Playground

Typescript

Alternatives considered
The actual situation I'm working with is in modeling a type-check diagnostic result from typescript's compiler api. As a workaround, I've added another type which omits the recursive field, and will have to flatten out the data into a plain array

Typical

struct Diagnostic {
    source: String = 0
    information: [DiagnosticInformation] = 1
}

struct DiagnosticInformation {
    category: DiagnosticCategory = 0
    code: S64 = 1
    optional file: File = 2
    optional start: U64 = 3
    optional length: U64 = 4
    message: DiagnosticMessage = 5
}

choice DiagnosticCategory {
    warning = 0
    error = 1
    suggestion = 2
    message = 3
}

choice DiagnosticMessage {
    text: String = 0
    chain: DiagnosticMessageChain = 1
}

struct DiagnosticMessageChain {
    text: String = 0
    category: DiagnosticCategory = 1
    code: S64 = 2
    # Recursive in typescript's compiler API definition. Flatten out before sending
    optional next: [DiagnosticMessageChainFlat] = 3
}

struct DiagnosticMessageChainFlat {
    text: String = 0
    category: DiagnosticCategory = 1
    code: S64 = 2
}

Taken from typescript 4.7.4 source code

export interface Diagnostic extends DiagnosticRelatedInformation {
        /** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */
        reportsUnnecessary?: {};
        reportsDeprecated?: {};
        source?: string;
        relatedInformation?: DiagnosticRelatedInformation[];
    }

 export interface DiagnosticRelatedInformation {
        category: DiagnosticCategory;
        code: number;
        file: SourceFile | undefined;
        start: number | undefined;
        length: number | undefined;
        messageText: string | DiagnosticMessageChain;
    }

 /**
     * A linked list of formatted diagnostic messages to be used as part of a multiline message.
     * It is built from the bottom up, leaving the head to be the "main" diagnostic.
     * While it seems that DiagnosticMessageChain is structurally similar to DiagnosticMessage,
     * the difference is that messages are all preformatted in DMC.
     */
    export interface DiagnosticMessageChain {
        messageText: string;
        category: DiagnosticCategory;
        code: number;
        next?: DiagnosticMessageChain[];
    }

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions