The example in the docs is clear and understandable:
insertMultipleLocations :: Statement (Vector (UUID, Double, Double)) ()
insertMultipleLocations =
Statement sql encoder decoder True
where
sql = "insert into location (id, x, y) select * from unnest ($1, $2, $3)"
encoder =
Data.Vector.unzip3 >$<
Contravariant.Extras.contrazip3
(Encoders.param $ Encoders.nonNullable $ Encoders.foldableArray $ Encoders.nonNullable Encoders.uuid)
(Encoders.param $ Encoders.nonNullable $ Encoders.foldableArray $ Encoders.nonNullable Encoders.float8)
(Encoders.param $ Encoders.nonNullable $ Encoders.foldableArray $ Encoders.nonNullable Encoders.float8)
decoder = Decoders.noResult
However, I’m struggling with finding a way to adapt to cases where the params aren’t a vector/list of tuples. For instance, something like this
data MyType = MyType { id_ :: UUID, x :: Double, y :: Double}
insertMultipleLocations :: Statement (Vector MyType) ()
insertMultipleLocations =
Statement sql encoder decoder True
where
sql = "insert into location (id, x, y) select * from unnest ($1, $2, $3)"
encoder = ???
decoder = Decoders.noResult
I could write a function myTypeToTuple :: MyType -> (UUID, Double, Double)
and compose it with unzip3
, but when the number of fields grow it becomes increasingly painful.
What patterns are people using for writing the encoder in such cases?