Use |? more?
I notice that we have a bunch of custom types that implement the same behaviour as our |? type (I actually just introduced a few!). I guess we should use the latter more?
Or perhaps we need a little policy? Like:
- If the spec introduces a named type (
type Foo = a | b) then make a type for it in Haskell with the same name. - If the spec uses a union type inline (
{ ... foo :: a | b ... }) then use|?
We were actually considering getting rid of |?, replacing it with custom types or something like a lightweight extensible sum. Nested (|?) leads to some horrible code, for example here: https://github.com/alanz/lsp/blob/28830a5339ec8276df02a9a14211e8bb70aec5f3/lsp-types/src/Language/LSP/VFS.hs#L204
I think we could probably do something with deriving via and generics-sop (like in this blog post: https://iceland_jack.brick.do/e28e745c-40b8-4b0b-8148-1f1ae0c32d43). If that sounds nice I could try it out.
I implemented something, not sure if its worth it: https://github.com/wz1000/haskell-lsp/commit/06f2c6c175e6b663ac03934f280dfe8b0017ad8c
/cc @bubba
Well, what I was hoping is we'd be able to do something like:
newtype UntaggedUnion a = UntaggedUnion a
instance (SOP.Generic a ...) => FromJSON a
instance (SOP.Generic a ...) => ToJSON a
data UsefulType = A A | B B
deriving stock Generic
deriving anyclass SOP.Generic
deriving (ToJSON, FromJSON) via UntaggedUnion
Which I think is nicer, since you can just use normal pattern matching everywhere and it only affects the serialization.
... but I'm stuck in the weeds of generics-sop :sweat_smile:
Do we need generics-sop for that? Wouldn't regular GHC.Generics be good enough?
I think it's hard to assert that the GHC.Generics Rep type has a particular shape, which we need here. We need to say "it's a union", i.e. a sum of single-argument constructors. I feel like I should be able to say this nicely in generics-sop but it's suspiciously absent from the library, which is making me wonder...
I made it work, here's a very hacked-together example. Most of this would go in a common module (and some if it could go in generics-sop tbh). The payload is being able to derive the instances for CompletionDoc: https://github.com/michaelpj/lsp/commit/3b9f174b194346da7d7a03be4437f42e701c4830
So the idea would be: always have custom types for such fields, and derive their aeson instances via UntaggedUnion if appropriate.
I think aeson already supports the UntaggedValue option for deriving instances for sums: https://hackage.haskell.org/package/aeson-1.5.4.0/docs/Data-Aeson.html#t:SumEncoding
Wow okay. Well that's a lot easier. Then I guess we can probably just use the TH deriving for aeson instances and use that setting?
@michaelpj could we close this one?
Well, we haven't done it! We still have a mix of |? and explicit union types using UntaggedValue. I did have a little look at it, but it's mostly just a) tedious b) annoying to come up with lots of names for these anonymous unions, and c) arguably a bit less legible when looking at the types. So I'm not really sure what to do.
irrelevant now