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

Switch to proto-lens #296

Merged
merged 17 commits into from
Oct 3, 2019
Merged

Switch to proto-lens #296

merged 17 commits into from
Oct 3, 2019

Conversation

tclem
Copy link
Member

@tclem tclem commented Sep 30, 2019

Fixes #89

Move away from our custom fork of proto3-* and use proto-lens instead. There are still some missing pieces here wrt to JSONPB encoding/decoding and the protobuf tooling setup that I'm working through.

@@ -87,7 +88,7 @@ data TermOutputFormat
parseTermBuilder :: (Traversable t, Member Distribute sig, ParseEffects sig m, MonadIO m)
=> TermOutputFormat -> t Blob -> m Builder
parseTermBuilder TermJSONTree = distributeFoldMap jsonTerm >=> serialize Format.JSON -- NB: Serialize happens at the top level for these two JSON formats to collect results of multiple blobs.
parseTermBuilder TermJSONGraph = termGraph >=> serialize Format.JSONPB
parseTermBuilder TermJSONGraph = termGraph >=> serialize Format.JSON
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ there will be conflicts with #300; hopefully pretty minor, but let me know if you’d like another pair of 👀 on anything.

@tclem tclem changed the title WIP: Switch to proto-lens Switch to proto-lens Oct 2, 2019
@tclem tclem requested a review from a team October 2, 2019 21:36
Copy link
Contributor

@robrix robrix left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some notes/comments. Good stuff!

cabal.project Outdated
location: https://github.com/joshvera/proto3-wire.git
tag: 84664e22f01beb67870368f1f88ada5d0ad01f56
location: https://github.com/tclem/proto-lens-jsonpb
tag: 2b4cd579144ea10c3bd36bd05122c233dc5334d7
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

@@ -304,6 +304,9 @@ library
, prettyprinter ^>= 1.2.1
, pretty-show ^>= 1.9.5
, profunctors ^>= 5.3
, proto-lens == 0.5.1.0
, proto-lens-jsonpb
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Do we need a version bound on this?
  2. Should these version bounds be using ^>= to express “up to the next major version”?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upon reflection we probably don’t need a version bound on proto-lens-jsonpb because we’re bringing it in with source-repository-package, but I think ^>= for proto-lens is a good idea either way.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We’ll eventually want to push proto-lens-jsonpb to Hackage so we can fix #16, so a version bound on both these is good to me.

Merge t@(In (a1, a2) syntax) -> diffAlgebra t (Merged (Just (MergedTerm (T.pack (constructorName syntax)) (ann a1) (ann a2))))
Patch (Delete t1@(In a1 syntax)) -> diffAlgebra t1 (Deleted (Just (DeletedTerm (T.pack (constructorName syntax)) (ann a1))))
Patch (Insert t2@(In a2 syntax)) -> diffAlgebra t2 (Inserted (Just (InsertedTerm (T.pack (constructorName syntax)) (ann a2))))
Merge t@(In (a1, a2) syntax) -> diffAlgebra t . DiffTreeVertex'Merged $ defMessage
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m having a lot of trouble understanding the control flow here, in part due to the combined use of $ and &. Can you check my understanding, please? To wit: we’re starting with the default message, setting a bunch of fields on it with the & … lines below, and finally wrapping it up in the DiffTreeVertex'Merged constructor and passing that to the algebra. Is that right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If so, I’m not sure I really love that idiom relative to using constructors directly, but perhaps that’s just not the way this approach works…?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it’s… it’s not the world’s most natural idiom in Haskell. But avoiding constructors and updating a default value through lenses lets you hide implementation details such as the _unknownFields datum attached to message types, which gracefully handles the presence of fields we don’t recognize or can’t decrypt. (proto3-suite just drops that information on the ground silently).

Remember that in protobuf records, every type is, essentially, an instance of Lower—given a message, every message field inside it is a Maybe defaulting to Nothing, every string field defaults to “”, and every numeric field defaults to 0. This means there’s always a sensible defMessage (similar in essence to lowerBound).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, excellent points all around. I wouldn’t mind if we could use a (smart) constructor (ideally generated) that would include all the fields except for _unknownFields, tho, e.g.:

thing :: Foo -> Bar -> Thing
thing foo bar = defMessage
  & P.foo .~ foo
  & P.bar .~ bar

Guessing I should file that upstream?

Remember that in protobuf records, every type is, essentially, an instance of Lower

This is such a good way of describing this ☝️ 👏

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upon reflection, it occurs to me that it’s probably a good thing that the idiom for the proto-lens code is substantially different than that for our typical internal types. Tho it does kind of reinforce my suspicion that this is a hint that this code should be working on internal types if possible which we bridge to wire types. (Tho that would involve significant restructuring to handle the JSON formatting of the wire types, so maybe I should just shush and let you get on with things.)

Either way, I think any of the above that we decide to tackle can wait for future PRs. This is great work, @tclem.

& P.maybe'span .~ ann a1
Patch (Insert t2@(In a2 syntax)) -> diffAlgebra t2 . DiffTreeVertex'Inserted $ defMessage
& P.term .~ T.pack (constructorName syntax)
& P.maybe'span .~ ann a2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m additionally wondering if this is a hint that we should be producing some internal, intermediate type that we bridge to the wire types separately. What do you think?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's a very good direction to move in general.

I think these graph representations essentially are a translation of internal types to output types that are suitable for serialization or display.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that’s a good point. I think superficially I was meaning “move this lens code somewhere else” but a) that’s a different story altogether, b) it looks like it might be super inconvenient, and c) I really don’t think we ought to hold up this PR just for that.

let root = vertex (DiffTreeVertex (fromIntegral i) (Just a))
let root = vertex $ defMessage
& P.diffVertexId .~ fromIntegral i
& P.maybe'diffTerm ?~ a
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m going to have to learn to read lens, I guess.

@@ -0,0 +1,842 @@
-- Code generated by protoc-gen-jsonpb_haskell 0.1.0, DO NOT EDIT.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

proto-lens doesn't currently support encoding/decode to json so this is a bit of a hack to preserve semantic's existing API and functionality. This file gets generated by https://github.com/tclem/proto-lens-jsonpb, a protoc plugin and haskell library for encoding/decoding jsonpb which I ported over from proto3-suite. See https://developers.google.com/protocol-buffers/docs/proto3#json for how protobufs are suppose to translate to json.

[ "service" .= (x^.service)
]

instance FromJSON PingRequest where
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are all orphan instances. A future alternative would be to contribute something to proto-lens so that this code is generated with the proto-lens-protoc plugin, but I'll leave that for another day.

& P.maybe'span .~ ann a1
Patch (Insert t2@(In a2 syntax)) -> diffAlgebra t2 . DiffTreeVertex'Inserted $ defMessage
& P.term .~ T.pack (constructorName syntax)
& P.maybe'span .~ ann a2
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's a very good direction to move in general.

I think these graph representations essentially are a translation of internal types to output types that are suitable for serialization or display.

Copy link
Contributor

@patrickt patrickt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing work, @tclem!

Copy link
Contributor

@robrix robrix left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work @tclem!

@@ -24,6 +24,7 @@ tmp/
*.hp
*.prof
*.pyc
/hie.yaml
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Been looking forward to merging just for this 😉

location: https://github.com/joshvera/proto3-wire.git
tag: 84664e22f01beb67870368f1f88ada5d0ad01f56
location: https://github.com/tclem/proto-lens-jsonpb
tag: e4d10b77f57ee25beb759a33e63e2061420d3dc2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this something we can/should push to hackage at some point?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We definitely can, I'm not sure we should just yet. Let's see if this approach works for us.

& P.path .~ path
& P.language .~ lang
& P.vertices .~ mempty
& P.edges .~ mempty
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it generally the case that these fields would be default-initialized to the same value as mempty?

Copy link
Member Author

@tclem tclem Oct 3, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we could remove the setting to mempty entirely.

@tclem tclem merged commit 05edc30 into master Oct 3, 2019
@tclem tclem deleted the proto-lens branch October 3, 2019 17:15
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

Successfully merging this pull request may close these issues.

Investigate feasibility of switching to proto-lens for protobuf serialization.
3 participants