agrafix / elm-bridge

Haskell: Derive Elm types from Haskell types
BSD 3-Clause "New" or "Revised" License
101 stars 27 forks source link

Converting sum types #21

Closed MartinPotier closed 6 years ago

MartinPotier commented 6 years ago

Hi,

I'm trying to convert my Haskell Either a b to an Elm Either a b but the encoder and decoder for this are… weird. It creates jsonDecEither and jsonEncEither decoders that need 2 arguments! I'd expect a set of encoder/decoder per constructor. Are sum types supported?

Nice job by the way, for all the rest it's been working like a charm!

bartavelle commented 6 years ago

Sum types are indeed supported. It needs two arguments because of the lack of typeclasses in Elm. The arguments are the decoders for the a and b types!

MartinPotier commented 6 years ago

Still, the implementation of jsonDecEither/jsonEncEither is on me, right? I have to provide a way to choose what encoder to use.

Example:

The right side of (:=) is causing a type mismatch.

207|     "someConfigField" := jsonDecEither (Json.Decode.string) (jsonDecValue)
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(:=) is expecting the right side to be a:

    Json.Decode.Decoder a

But the right side is:

    Json.Decode.Value -> Either String Json.Decode.Value

Hint: It looks like a function needs 1 more argument.

Yeah, my right side should be an Either String Json.Decode.Value. Or should I wrap this in a Decoder?

jsonEncEither leftEnc rightEnc v = 
  case v of
    Left e  -> leftEnc e
    Right o -> rightEnc o

jsonDecEither leftDec rightDec v = 
  Json.Decode.success <|
  case (Json.Decode.decodeValue leftDec v) of
    Ok s -> Left s
    Err _ -> case (Json.Decode.decodeValue rightDec v) of
      Ok v -> Right v
      Err _ -> Left ""

The encoder part is OK because I can pattern match, but the decoder part is trickier, since I'm not allowed to peek and decide what constructor to use. The decoder I wrote here is wrong of course. But this may give you an idea of what I am trying to do.

bartavelle commented 6 years ago

But it really depends on how you encoded your sum type! What is its shape, in json, for both the left and right cases? Also, would it be generic (can you encode Either String String) ?

MartinPotier commented 6 years ago

Hmm, you are right, I should look into the encoding of the sum type. It was automatically derived using generics.

EDIT: Ok, new approach that may or may not work, using map. At least it passes the type checking (I'm using oneOf):

jsonEncEither leftEnc rightEnc v =
  case v of
    Left e  -> leftEnc e
    Right o -> rightEnc o
jsonDecEither : Json.Decode.Decoder a -> Json.Decode.Decoder b -> Json.Decode.Decoder (Either a b)
jsonDecEither leftDec rightDec =
  let liftLeftDec  = Json.Decode.map Left leftDec
      liftRightDec = Json.Decode.map Right rightDec
  in
  Json.Decode.oneOf [ liftLeftDec, liftRightDec ]

It all depends on the behaviour of oneOf. And of course Either String String is quite undefined here. It may always pick up a Left value…

MartinPotier commented 6 years ago

Closing this for now, as it may not be a problem of elm-bridge.

bartavelle commented 6 years ago

Just a note : you will usually need a tag of some sort for your encoding to be generic. For example, this could be decoded as List (Either String String):

[{"left":"bar"},  {"right":"foo"}]