# Elm Code Generation in Protoc-Gen-Elm

**URL:** <https://discourse.elm-lang.org/t/elm-code-generation-in-protoc-gen-elm/9039>\
**Category:** Request Feedback\
**Created:** [March 30, 2023, 10:39am UTC](https://discourse.elm-lang.org/t/elm-code-generation-in-protoc-gen-elm/9039 "2023-03-30T10:39:53Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![andreasmolitor](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/andreasmolitor/32/4384_2.png) [@andreasmolitor](https://discourse.elm-lang.org/u/andreasmolitor)\
**Post date:** [March 30, 2023, 10:39am UTC](https://discourse.elm-lang.org/t/elm-code-generation-in-protoc-gen-elm/9039/1 "2023-03-30T10:39:53Z")

</div>

Hi 🙂

I’m currently maintaining the [protoc-gen-elm](https://github.com/andreasewering/protoc-gen-elm) protoc plugin and stumbled across an issue/limitation in the elm module system. Because all solutions that I can come up with kind of suck in one way or another, I thought I’ll make a post here to get some ideas and feedback.

**What does protoc-gen-elm do?**

It generates .elm files from .proto files, which contain decoders and encoders to convert Elm datatypes to the protocol buffers wire format.

Given a proto file like this:

```auto
syntax = "proto3";

message Test {
  string fieldOne = 1;
  bool fieldTwo = 2;
}

```

something like this is generated:

```auto
import Protobuf.Encode (Encoder)
import Protobuf.Decode (Decoder)

type alias Test {
  fieldOne : String,
  fieldTwo : Bool
}

encodeTest : Test -> Encoder
decodeTest : Decoder Test

```

**Nested messages**

In .proto files, messages are also a namespace where we can group other declarations.  
For example:

```auto
enum OuterEnum {
  A = 0;
  B = 1;
}

message Outer {
  message Inner {}
  enum InnerEnum {
    One = 0;
    Two = 1;
  }
  oneof msg {
    Inner inner = 1;
    InnerEnum innerEnum = 2;
    OuterEnum outerEnum = 3;
  }
}

```

Now the question becomes: What elm code do we generate from this?  
The following is what I want to be able to write:

```auto
encodedOuter = encodeOuter { msg = Just <| Outer.inner {}}
encodedOuter2 = encodeOuter { msg = Just <| Outer.innerEnum Outer.One }
encodedOuter3 = encodeOuter { msg = Just <| Outer.outerEnum B }

```

or something similar.

The issue with this code is that to handle the scopes, we used two modules:  
The `Outer` module which defines all the things inside the scope of the `Outer` message and the  
other, in this example implicitely global, scope, which defines the `Outer` type, the `encodeOuter` function and the `OuterEnum` union type.

In order to define a type for `Outer.outerEnum` we need to be able to refer to the `OuterEnum` type from the `Outer` module. But in order to define the type `Outer`, we need to be able to refer to the types inside of the `Outer` module. We have just ran into a cyclic dependency!

**Solutions**

The current solution implemented in protoc-gen-elm is to scrap the proto namespacing stuff and just

1. hope for no collisions
2. Try to do manual namespacing by adding prefixes like `Outer_Msg_InnerEnum`

The general solution for cyclic dependencies is to put the cyclic stuff into its own module and then import from there. Unfortunately, this comes at a great disadvantage:

While we can re-export functions and type aliases with wrapper functions and more type aliases,  
the fun stops at union types. I am not aware of any way to re-export the constructor function `Outer_Msg_InnerEnum` as something more readable in another module. Obviously I can create a wrapper function but I lose the ability to pattern match.

The most recent idea that I’ve had is to generate the “ugly” type in a big module with all the stuff in it in order to have no cyclic dependency issue and to generate a nice seperate type in another module together with conversion functions between the two. That would make the generated code kind of confusing, but at least the code on the user side would look pretty good.

**Other ideas?**

What do you think? Have I missed something obvious? Are there other ways of solving the issue?  
Is the original way of namespacing with “\_” the more understandable solution?

For some context, here is the issue where my exploration started: [Name clashes · Issue #151 · andreasewering/protoc-gen-elm · GitHub](https://github.com/andreasewering/protoc-gen-elm/issues/151)

Thanks!

---

<div class="post-metadata">

**Author:** ![andreasmolitor](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/andreasmolitor/32/4384_2.png) [@andreasmolitor](https://discourse.elm-lang.org/u/andreasmolitor)\
**Post date:** [April 1, 2023, 1:21pm UTC](https://discourse.elm-lang.org/t/elm-code-generation-in-protoc-gen-elm/9039/2 "2023-04-01T13:21:44Z")

</div>

I decided for generating mapping methods for union types and using aliases for product types.

The changes are released in a beta version (3.0.0-beta.0)

---

<div class="post-metadata">

**Author:** ![system](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/1X/50a05e53677a2c3b47776d7abd0f113eb50193a1.png) [@system](https://discourse.elm-lang.org/u/system)\
**Post date:** [April 11, 2023, 1:22pm UTC](https://discourse.elm-lang.org/t/elm-code-generation-in-protoc-gen-elm/9039/3 "2023-04-11T13:22:10Z")

</div>

Closed on April 11, 2023, 1:22pm UTC

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