Elm Radio Episode 25 - elm-ts-interop

I’m very pleased to announce a new tool that I’ve been working on for months! I’ll be sharing some learning resources and material in the next couple of weeks about how to use the tool. Today, we have a podcast episode discussing the new tool

More to come! You can check out the pro tool and sign up for an early access discount at http://elm-ts-interop.com/.

11 Likes

Exciting stuff. I have not used any TS interop yet in my experiments, but this tool seems very useful for solving the problem of synchronizing types!

I had to listen to the Elm-radio episode before I understood that this is a free tool with an optional professional upgrade. I think this should be more visible on both the github repo and the tool site, as I initially felt the barrier of entry was too high with the upfront cost.

After I had listened to the podcast the funding model made more sense to me, and it seems like a good investment in the tooling for a company working with both Elm and TS.

1 Like

I’m working on incrementally converting a project to use this, and I’m not all the way there yet, but this looks really cool so far.

I also wasn’t sure what the difference between the regular and pro experiences were. I purchased pro basically because I was basically like “I don’t understand the difference, but it’s Elm and it’s the graphql tooling person, I’m sure it’ll be great”.

My understanding is that the difference with pro is that you get autogeneration of both a typescript definitions file and an Elm file with functions to decode flags and send and receive messages through ports. (plus the ts-to-elm converter page)

If you don’t have pro, do you just figure out how to get the types out of the Definitions.elm file yourself? I think that distinction would be useful — like what’s the step-by-step comparative workflow for converting an existing app to use this library with a non-pro version vs with a pro version.

1 Like

Also, curious question:

I have a decoder in my app that’s a bit weird. Essentially, it decodes a list of items representing a linked list, and puts the items in their specified order as an andThen step of the decoding. The original decoder looks roughly like this:

linkedList : Decoder (List { item | id: Id, next : Maybe Id }) -> Decoder (List { item | id: Id, next : Maybe Id })
linkedList itemDecoder =
    itemDecoder
        |> Decode.list
        |> Decode.andThen
            (\list ->
                case sortLinkedList list of
                    Ok sortedList ->
                        sortedList

                    Err error ->
                        Decode.fail error
            )

I realize this is probably a somewhat unusual case, but I haven’t been able to figure out how to recreate this using the andThen/andThenInit/andThenDecoder functions in dillonkearns/elm-ts-json. Is it possible to do something like this with this library?

1 Like

Something like this should do what you want.

    list itemDecoder
        |> andThen
            (andThenInit
                (\failDecoder decodedList ->
                    case sortLinkedList decodedList of
                        Ok sorted ->
                            TsDecode.succeed sorted

                        Err error ->
                            TsDecode.fail error
                )
            )

Maybe we can discuss further in a GitHub issue, but I have some more thoughts on this design. In particular, it could be improved by using an opaque type, like the union type builder does. But it probably should have a way to call succeed or fail directly. The reasons why are a bit in the weeds, but the bottom line is it provides stronger guarantees about the type information you get.

Yes, apologies for not making it clear right away in the podcast episode. I meant to start out with it, but we got caught up in all the details. The good news is that all the details we talked about before talking about the pro version are part of the free edition. But I agree, it’s good to be clear that there is also a pro plan available.

I didn’t get a chance to include it in the in the landing page yet, but my plan is to have a button to toggle between the setup workflow to compare how it looks between the free and pro versions. And also to have a feature comparison table. Still working on some of those details, but they’re definitely important details that I will get to soon.

Thanks for the feedback! And I hope both the free and pro versions are a great experience! :slight_smile:

1 Like

For anyone who is curious, I started a follow-up GitHub issue discussing the andThen API more here: Use opaque type for andThen, allow succeed and fail · Issue #4 · dillonkearns/elm-ts-json · GitHub

1 Like

No, I found the elm-radio episode great. And felt no issue with the Pro stuff coming late in the podcast.

I just did not understand that there is a “feature-complete” free subset before I heard the podcast. I assumed the podcast episode would end up with: “BUT, the catch is that all this is a paid product”. What it really ended with was, “all this is free, but I have made some pro tools to make the process easier” which I found great.

2 Likes

Hi Dillon,

Does this tool always go from Elm → Typescript, or can it go the other way around?

The reason I ask, is I have been using AWS CDK recently, which has typescript bindings. If I could reverse engineer those, then I could probably codegen an Elm version of the CDK - or at least a good starting point for writing one. Not a project I am aiming to tackle at the moment, as its too much of a rabbit hole to get sucked down just now, but I am curious if Elm bindings can be generated for existing Typescript code? It would be amazing to be able to define cloud infrastructure as an Elm DSL, got to be a better language for doing that than Typescript or even Terraform (which is useful but ugly language).

Good question! The Pro tier includes a scaffolding tool for exactly this purpose. It gets you started with an Encoder and Decoder based on a TypeScript type as input. I’m still working on getting some video tutorials and demos to showcase it better, but for now here’s a small gif demo (this is live in production for pro users).

And here’s the full resulting generated code from that input type:

type User
    = Admin { adminId : Float }
    | Guest
    | Regular { first : String, last : String }

userEncoder : Encoder User
userEncoder =
    Encode.union
        (\vAdmin vGuest vRegular value ->
            case value of
                Admin data ->
                    vAdmin data

                Guest ->
                    vGuest

                Regular data ->
                    vRegular data
        )
        |> Encode.variant
            (Encode.object
                [ Encode.required "role" identity (Encode.literal <| JE.string "admin")
                , Encode.required "adminId" .adminId Encode.float
                ]
            )
        |> Encode.variantLiteral (JE.object [ ( "role", JE.string "guest" ) ])
        |> Encode.variant
            (Encode.object
                [ Encode.required "role" identity (Encode.literal <| JE.string "regular")
                , Encode.required "first" .first Encode.string
                , Encode.required "last" .last Encode.string
                ]
            )
        |> Encode.buildUnion


userDecoder : Decoder User
userDecoder =
    Decode.oneOf
        [ Decode.succeed (\() adminId -> Admin { adminId = adminId })
            |> Decode.andMap (Decode.field "role" (Decode.literal () (JE.string "admin")))
            |> Decode.andMap (Decode.field "adminId" Decode.float)
        , Decode.literal Guest (JE.object [ ( "role", JE.string "guest" ) ])
        , Decode.succeed (\() first last -> Regular { first = first, last = last })
            |> Decode.andMap (Decode.field "role" (Decode.literal () (JE.string "regular")))
            |> Decode.andMap (Decode.field "first" Decode.string)
            |> Decode.andMap (Decode.field "last" Decode.string)
        ]

The really nice thing about the scaffolding tool is that it will start you out with an Encoder or Decoder that yields or accepts that TypeScript type. But from there, you take the code and own it, and as you make changes the changed types are reflected in the TypeScript and Elm types, so it’s always in sync.

2 Likes

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.