lsp icon indicating copy to clipboard operation
lsp copied to clipboard

Use |? more?

Open michaelpj opened this issue 4 years ago • 13 comments

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?

michaelpj avatar Feb 28 '21 12:02 michaelpj

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 |?

michaelpj avatar Feb 28 '21 12:02 michaelpj

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

wz1000 avatar Feb 28 '21 12:02 wz1000

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.

michaelpj avatar Feb 28 '21 16:02 michaelpj

I implemented something, not sure if its worth it: https://github.com/wz1000/haskell-lsp/commit/06f2c6c175e6b663ac03934f280dfe8b0017ad8c

/cc @bubba

wz1000 avatar Feb 28 '21 18:02 wz1000

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:

michaelpj avatar Feb 28 '21 18:02 michaelpj

Do we need generics-sop for that? Wouldn't regular GHC.Generics be good enough?

wz1000 avatar Feb 28 '21 18:02 wz1000

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...

michaelpj avatar Feb 28 '21 18:02 michaelpj

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

michaelpj avatar Mar 01 '21 09:03 michaelpj

So the idea would be: always have custom types for such fields, and derive their aeson instances via UntaggedUnion if appropriate.

michaelpj avatar Mar 01 '21 09:03 michaelpj

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

wz1000 avatar Mar 01 '21 13:03 wz1000

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 avatar Mar 01 '21 16:03 michaelpj

@michaelpj could we close this one?

jneira avatar Jun 22 '21 07:06 jneira

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.

michaelpj avatar Jun 23 '21 17:06 michaelpj

irrelevant now

michaelpj avatar Jul 13 '23 15:07 michaelpj