For quite a while me and @MartinS have been working on a little project called elm-review-derive. I think it’s at a stage where it can be released at a sort of beta level of expectations. The hope here is that it can be useful for a number of people, but also get more people interested in this kind of thing and provide some quality feedback about the tool.
What is it?
It’s a tool that provides ad-hoc code generation inside your project based on type signatures. It searches for top-level functions whose body is a Debug.todo
and that have a type signature the tool recognizes, such as:
myDecoder : Decode SomeType
myDecoder =
Debug.todo ""
It will mark any such rule as an error (this is an elm-review rule after all), and attempt to provide a fix. It does that by recursively walking down the definition of the target type (SomeType
in this example) and generating definitions to match the type.
So if SomeType
looks like this:
type alias SomeType =
{ name : String
, age : Int
, status : Status
}
type Status =
Approved | Rejected
then the first fix provided will change the code to:
myDecoder : Decoder SomeType
myDecoder =
Decode.map3
SomeType
(Decode.field "name" Decode.string)
(Decode.field "age" Decode.int)
(Decode.field "status" decodeStatus)
decodeStatus : Decoder Status
decodeStatus =
Debug.todo ""
However, elm-review keeps running rules until they stop finding issues, so the rule will continue running and fills out decodeStatus
like so:
myDecoder : Decoder Status
myDecoder =
Decode.field "tag" Decode.string
|> Decode.andThen
(\ctor ->
case ctor of
"Approved" ->
Decode.succeed Approved
"Rejected" ->
Decode.succeed Rejected
_ ->
Decode.fail "Unrecognized constructor"
)
This step by step generation allows for interactive use (intended to be used with --fix
rather than --fix-all
), where you can pause generation at any point and adjust what’s going on before you keep going.
For instance perhaps the generated decoders don’t quite suite your preference. But the advantage of this tool is that it works in place in your editor and it’s often easier to quickly edit the generated code rather than type it out from scratch.
What’s it like to code with this?
To give you an idea what a workflow of using this tool can look like, I’ve recorded a video of me adding a few features to an existing application using this tool:
(Sorry for the occasional mumbling, I’ve gained some respect for streamers as talking and programming at the same time turns out to be harder than expected).
What kind of things can it generate?
At the moment, it can help with the following sorts of type signatures:
Serialize.Codec e MyType
Codec.Codec e MyType
Csv.Decode.Decoder MyType
String -> Maybe MyType
Fuzz.Fuzzer MyType
Json.Decode.Decoder MyType
MyType -> Json.Encode.Value
List MyType
Random.Generator MyType
MyType -> String
See the README for more details.
Can it be extended?
Yes, it is possible and not too difficult to write additional generators that can generate additional kinds of structures. The documentation should be reasonably helpful, or you can browse the existing code generators as examples.
Future plans
First, we’d like to shake out any existing bugs and improve the error reporting capability (sometimes it can be kind of confusing why code generation doesn’t work or how to fix it).
Second, I’d like to expand the kinds of type signatures we can reasonably operate on. For instance, I’d like to be able to generate meaningful map
functions, or generate functions that can transform one type into another type.
Finally, at the moment the only input the code generation context can take is type information. I think it would be interesting to also allow other forms of input from the surrounding code. For one the string from the Debug.todo
could be used in interesting ways. Html could be transformed into elm/html or graphql queries could be matched to the desired types in the type signatures to automatically generate elm-graphql code??? Who knows what else can be done?
Anyway, I hope you can give this tool a try and I’ll be happy to hear any feedback you may have.