haskell-opaleye
haskell-opaleye copied to clipboard
Type Family `U`
Currently, when you specify that you want the Nulls version of a table defined with type families, you get every field's SqlType wrapped in Nullable. It'd be nice to also have the ability to get every Haskell type wrapped in Maybe.
My use case is that I wanted to define endpoints to update various tables in my database. Once a row is created, all the fields in the update payload should be a Maybe. If they're Justs, then we updated that field if Nothing then we used the previous value for that field. I was able to implement a type family instance like so:
type instance TF.A ('TF.H TF.NullsT) ('TF.C '(h, o, n)) = Opaleye.Column (Opaleye.Nullable o)
data NullsHT
type NullsH = 'TF.H NullsHT
type instance TF.A ('TF.H NullsHT) ('TF.TC '(t, b)) = TF.A ('TF.H NullsHT) ('TF.C t)
type instance TF.A ('TF.H NullsHT) ('TF.C '(h, o, TF.NN)) = Maybe h
type instance TF.A ('TF.H NullsHT) ('TF.C '(h, o, TF.N)) = Maybe (Maybe h)
and use NullsH.
One thing to note in my implementation is that if the field is optional and nullable then it's wrapped in two Maybes, one for the optionality and one for the nullability. Not sure if this is something specific to my use case or how it should be generally implemented.
Hmm, I'm not sure I understand. I thought this was what WT was for. It only has one layer of Maybe and the Haskell type itself should have the other. If WT doesn't work for you then perhaps you can provide a small concrete example of what you're trying to do.
After thinking a bit more, I think what I've been looking for is something like Update rather than Write.
My smaller, more concrete use case is this:
Let's say we have a table like:
data TableT f
= Table
{ id :: TableRecordField f UUID SqlUuid NN Req,
name :: TableRecordField f Text SqlText NN Opt,
}
We can define a few concrete types like so:
type Table =
TableT H
type TableField =
TableT O
type TableFieldWrite =
TableT W
With these, we can easily use the Haskell Types like normal with Table, use the Field types with TableField and use req/opt with TableFieldWrite. What I'm looking for is a way to update a fields. My requirements for updating are that we need all fields to be optional. If a field is omitted, the value is not changed. There's two parts to this I think, one type for resolving this as a Haskell type and one for a Field type. In my use case Haskell type would gotten from the client, and the Field type would be to perform the update itself.
If we look at how WT is implemented, we see:
type instance A ('H WT) ('TC '(t, Req)) = A ('H OT) ('C t)
type instance A ('H WT) ('TC '(t, Opt)) = Maybe (A ('H OT) ('C t))
Which is great for writing to a field. However, for updating a field I think I'm looking for something like this.
-- Wrap everything in a Maybe for optionality, then letting HT handle everything else
data UH
type instance A ('H UH) ('TC '(t, b)) = Maybe (A ('H HT) ('C t))
type U = 'H UH
This would make all fields optional in the outer maybe, then we could set null ability with the inner maybe (if a field is optional). Then for our table
type TableUpdate =
TableT U
I think what I'm looking for in this issue is essentially resolving to Haskell type to represent both optionality and nullability.
Aside If this were to be added, it also makes it seem like there should be something similar for running updated in Opaleye too. Something like:
-- Wrap everything in a Maybe for optionality, then letting WT handle everything else
data UT
type instance A ('H UT) ('TC '(t, b)) = Maybe (A ('H WT) ('C t))
I know there's been discussion on this before that resulted in updateEasy, but it feels to me that that's still confusing when dealing with optionality vs. nullability in updates in Opaleye.
It would be really cool to have something like the following when updating fields in Opaleye.
data Optional a
= Present a -- Set to this value
| Absent -- Set to current value
| Default -- Set to default value
| Null -- Set to null
-- Inner Optional for default/nullability
data UHT
type instance A ('H UHT) ('C '(h, o, n)) = Optional h
-- Outer maybe for optionality
data UT
type instance A ('H UT) ('TC '(t, b)) = Maybe (A ('H UHT) ('C t))