Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue decoding similar message types #117

Open
IvantheTricourne opened this issue Nov 20, 2019 · 3 comments
Open

Issue decoding similar message types #117

IvantheTricourne opened this issue Nov 20, 2019 · 3 comments

Comments

@IvantheTricourne
Copy link

IvantheTricourne commented Nov 20, 2019

Say we have 2 message types defined in Haskell and attempt to write codec instances for each:

data Foo = Foo
  { fooVal :: Text
  , fooValToo :: Text
  } deriving (Eq, Show, Generic)

instance Message Foo
instance Named Foo
instance HasCodec Foo

data Bar = Bar
  { barVal :: Text
  } deriving (Eq, Show, Generic)

instance Message DeleteName
instance Named DeleteName
instance HasCodec DeleteName

Let's assume HasCodec is a type class that provides the proper encode/decode functionality for a type. Next, we try to wrap both messages in a sum type that has its own HasCodec type:

data FooBarMessage =
    NFoo Foo
  | NBar Bar
  deriving (Eq, Show, Generic)

instance HasCodec FooBarMessage where
  decode bs =
    fmap NFoo (decode bs) <>
    fmap NBar (decode bs)
  encode = \case
    NFoo msg -> encode msg
    NBar msg -> encode msg

Currently, I have NBar messages being incorrectly decoded as NFoo messages under this context. Is this a known behavior? Or am I perhaps missing/misusing something? Is there any constraint on deriving Generic for sum types?

@Gabriella439
Copy link
Contributor

@IvantheTricourne: Encoded protobuf messages are schema-free, meaning that you cannot necessarily infer from the encoded value which type they should decode into. For example, an empty ByteString will always successfully decode as any protobuf value (with all fields set to the default/empty value).

This implies that you need to know out-of-band which type you expect to decode into rather than inferring which type by trying to decode each candidate type in succession.

@IvantheTricourne
Copy link
Author

IvantheTricourne commented Nov 22, 2019

Thanks for the reply @Gabriel439

I wrote the equivalent protobuf file for the example HS above:

syntax="proto3";
package TestProtoOneofImport;
message FooBar {
  oneof value {
    Foo foo = 1;
    Bar bar = 2;
  }
}

message Foo {
  string val = 1;
  string val_too = 2;
}

message Bar {
  string val = 1;
}

This has the semantic equivalent of representing a sum type over a set of messages as another message type. In this way, the codecs for each type are simply the bytestring encoders/decoders from the Message type instance. Generating haskell from the above proto snippet, we get this decode definition for the FooBar sum type:

decodeMessage _
          = (Hs.pure FooBar) <*>
              (HsProtobuf.oneof Hs.Nothing
                 [((HsProtobuf.FieldNumber 1),
                   (Hs.pure (Hs.fmap FooBarValueFoo)) <*>
                     (Hs.coerce @(_ (HsProtobuf.Nested Test.Foo))
                        @(_ (Hs.Maybe Test.Foo))
                        HsProtobuf.decodeMessageField)),
                  ((HsProtobuf.FieldNumber 2),
                   (Hs.pure (Hs.fmap FooBarValueBar)) <*>
                     (Hs.coerce @(_ (HsProtobuf.Nested Test.Bar))
                        @(_ (Hs.Maybe Test.Bar))
                        HsProtobuf.decodeMessageField))])

So, assuming this haskell snippet does successfully decode FooBars, what's the possibility of doing it the other way generically (i.e., Haskell types -> protobuf codecs)?

@Gabriella439
Copy link
Contributor

@IvantheTricourne: This package supports deriving instances from Haskell types using GHC generics (i.e. deriving (Generic, Message)), but that does not yet support sum types. However, as far as I know there's no particular reason we don't yet support deriving the encoder/decoder sum types: we just never used it up until now so we haven't implemented it yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants