elm-bridge
elm-bridge copied to clipboard
General questions about design of elm-bridge
I'm extending this library to auto-generate Elm API wrappers based on Servant type-signatures [1]. I've pretty much wrapped my head around the internals, but am scratching my head over the following:
- The philosophical / conceptual difference between
ETypeDefandEType. It seems to me thatETypeis some sort of Elm-compatible subset ofTH.Type, but I'm not so sure aboutETypeDef. deriveElmuses TH to generate instances forIsElmDefinition. However, there are noIsElmDefinitioninstances for things likeStringorBool. It this because you don't want to have "global" Haskell <=> Elm type-mappings forced upon users of this library, allowing them, instead, to make a choice on a case-by-case basis by usingmakeModuleContentWithAlterationsinfra?- Even then, if as an end-user, I want to define an
IsElmDefinitionfor my app, how would I do that for something likeString/Bool? How does one write a sensible implementation forcompileElmDef :: Proxy a -> ETypeDef? What value should this function return? The only possible option isETypePrimAlias, but I'm not sure how that would work? (this should give you some context about why I'm asking the very first question).
[1] Btw, are you open to a PR for this functionality? I don't see the point in releasing yet another elm <=> haskell library.
-
ETypesrepresent "type signatures", whereasETypeDefrepresent what the time is. -
This library tries to write code for the user's type. The rationale was that when an endpoint returns
Bool, the user would just use the decoder fromElminstead of generated code. Of course this doesn't work too well when generating code for whole APIs. ... -
For your particular problem, I am afraid there is no great solution. It is indeed ETypePrimAlias that makes sense here, and it should alias to
StringandBoolon the Elm side.
I don't know if this terse answers help you, and I am personally open to PR. I am sure @agrafix is too :) I think this library might need a good rework to suit your needs though ...
One thing that would be nice would be a generic Elm AST pretty printer, instead of the terrible string manipulation that happen all over the place. Would you have that in your sleeves? :)
@bartavelle Thanks for the clarification on ETypeDef vs EType. I'll rephrase my understanding again:
ETypeis trying to describe the structure of a new type that needs to be "written / generated" on the Elm sideETypeDefis trying to specify which existing Elm type should correspond to a given Haskell type
A related question: any reason why EAlias and ESum contain only a subset of the fields of Aeson.Options? Isn't it better to simply have the entire Aeson.Options record as one of the fields?
So, conceptually, here's what is required (or may already be existing):
- A way to specify Haskell <=> Elm type correspondence
- A way to specify Elm-type <=> Elm-json-codec correspondence
- A way to specify which "new" types need to be generated on the Elm side (basically custom Haskell types that do not correspond to anything idiomatic / standard on the Elm side)
- A way to specify which new Elm JSON codecs need to be generated
One thing that would be nice would be a generic Elm AST pretty printer, instead of the terrible string manipulation that happen all over the place. Would you have that in your sleeves? :)
This is probably the last thing that I'll attack. Upon a cursory glance, I wasn't able to find a "blessed" Elm code generator. Also, I'm also internally debating if it is better to stick to string-based templates for Elm code-gen to allow library users to modify them easily. For example, users might want an easy way to modify the Elm api-wrappers that will be generated.
For the option records, it is probably that those are parameters that have been added recently (or at least, recently enough), and that they are not supported by the library yet.
As for the difference between ETypeDef and EType I would have to read the code to tell you their exact meaning, so your guess is as good as mine right now ;)
I think I found something for safer Elm code gen - http://hackage.haskell.org/package/language-elm
That would be perfect!
@saurabhnanda we're also interested into this, alongside with https://www.reddit.com/r/haskell/comments/9zq14v/state_of_servantelm/
@domenkozar Could you check out the issue I opened here? https://github.com/agrafix/elm-bridge/issues/41
I'm not too familiar with how people are using elm-export or elm-bridge, and what their short comings are, but it's possible a sturdier core could help move things along.
@agrafix @bartavelle is it alright if we use this thread to discuss evolution of this library?
I've gotten a basic POC of using Servant.Foreign to generate an Elm API for a given Servant API. However, I am unable to come up with a good analog for the ToHttpApiData & FromHttpApiData type-classes of Servant. IIUC any type being "injected" in the URL (either as a path-segment OR as a query-param) needs to implement these type-classes to convert values of that type to/from Text.
How does one implement something like this, fairly automagically on the Elm side? Challenges:
- In Elm 0.19
toStringhas been moved toDebugand will not be available once you build the Elm project for production. Otherwise one possible solution could've been to usetoString-- but it may not parse correctly on the Haskell side. - Use a "helper module" on the Elm side which implements
toUrlSegmentBool,toUrlSegmentDouble,toUrlSegmentInt,toUrlSegmentTime,toUrlSegmentUTCTime, etc. But this would need to be manually kept in sync with Haskell code at http://hackage.haskell.org/package/http-api-data-0.4/docs/src/Web.Internal.HttpApiData.html#ToHttpApiData - which mostly usesshowinstances for all the common types. - What about custom types, especially newtypes? In our code, all primary keys are a newtype, eg.
newtype PK a = PK Int, and they're using in URL segments a lot.GET /users/:userId. How many differenttoUrlSegment*functions, on the Elm side, would one need to define to handle something like this?
/cc @domenkozar @mitchellwrosen
servant-elm currently does the following...
https://github.com/mattjbray/servant-elm/blob/9c8a6c289877408581462de9e87701b46832cf02/src/Servant/Elm/Internal/Generate.hs#L431-L440
... but has implemented a hard-coded list of type <=> string conversion functions for 0.19:
https://github.com/mattjbray/servant-elm/pull/45/files#diff-7e13462cc66d19b6a0d2a0ddcd7e8864R494
Okay! I've got a PoC in place which can do the following...
...given the following Servant API...
data Routes route = Routes
{ rRunCode :: route :- "runCode" :> "randomUrlSegment" :> Capture "id" Int :> Capture "order" String :> QueryParam "tryingEither" (Either Int Bool) :> QueryParam "someOtherParam" Bool :> ReqBody '[JSON] (Maybe InterpreterInput) :> Post '[JSON] InterpreterOutput
} deriving (Generic)
... it generates the following Elm API wrapper...
postrunCoderandomUrlSegmentbyidbyorder : Msg -> Int -> String -> Maybe (Either (Int) (Bool)) -> Maybe (Bool) -> Maybe (InterpreterInput ) -> Cmd Msg
postrunCoderandomUrlSegmentbyidbyorder msg0 id3 order4 tryingEither5 someOtherParam6 body8 =
Http.post { url = Url.absolute ["runCode", "randomUrlSegment", String.fromInt id3, identity order4] <| List.filterMap identity [toUrlSegmentMaybe (toUrlSegmentEither (String.fromInt) (toUrlSegmentBool)) tryingEither5, toUrlSegmentMaybe (toUrlSegmentBool) someOtherParam6], body = Http.jsonBody <| jsonEncMaybe (jsonEncInterpreterInput) body8, expect = Http.expectJson msg0 jsonDecInterpreterOutput}
Shall I bring this into a PR-able form? Are you alright with introducing a dependency on servant?
I personally use servant anyway, and I suppose most people do (?). It would be nice to ask @agrafix though, as he perhaps still uses this project?
@agrafix has been awfully quiet lately. Is he online on IRC / Reddit these days?
RE: Elm and http-api-data
I have hit the same issue and it's possible to use generics, but as soon as there is a manual instance in Haskell, something like http://blog.stermon.com/articles/2018/04/09/elm-stringeable-types-library-for-elm-019.html would work.
No idea! To be honest, I do not use this package anymore either, as I have stopped using Elm. I will try to keep maintaining it, and have a couple things to do on it, but this will have to wait for January ...
@bartavelle Moved on to PureScript? :)
@mitchellwrosen moved out of frontend, but last personal project was purescript + wasm indeed ;)