Open cdepillabout opened 3 years ago
After playing around with it a little more, I was able to find a hacky, unsafe way to do what I want:
unsafeRowSelect
:: forall row2 row1 s a
. SqlType a
=> Row s row1
-> Selector row1 a
-> Assignment s row2
unsafeRowSelect r selec = coerce selec := r ! selec -- I think the coerce here is the unsafe part
class MapOverSelectors a t where
mapOverSelectors
:: forall s u
. (forall x. SqlType x => Selector t x -> Assignment s u)
-> a
-> [Assignment s u]
instance (MapOverSelectors a t, MapOverSelectors b t) => MapOverSelectors (a :*: b) t where
mapOverSelectors
:: forall s u. (forall x. SqlType x => Selector t x -> Assignment s u)
-> (a :*: b)
-> [Assignment s u]
mapOverSelectors f (a :*: b) = mapOverSelectors f a <> mapOverSelectors f b
instance SqlType a => MapOverSelectors (Selector t a) t where
mapOverSelectors
:: forall s u
. (forall x. SqlType x => Selector t x -> Assignment s u)
-> Selector t a
-> [Assignment s u]
mapOverSelectors f s = [f s]
unsafeRowToAssignments
:: forall u s t
. (Relational t, MapOverSelectors (Selectors t) t, GSelectors t (Rep t))
=> Table t
-> Row s t
-> [Assignment s u]
unsafeRowToAssignments tbl row =
let allSelectors = selectors tbl
in mapOverSelectors (\s -> unsafeRowSelect row s :: Assignment s u) allSelectors
I use it like the following:
f :: m [Bar]
f = query $ do
fooRow <- select (fooTable :: Table Foo)
pure $ new $
(unsafeRowToAssignments fooTable fooRow :: [Assignment _ Bar]) <>
[ #c := 3
, #d := 4
]
This works, but it is unsafe. I have to manually make sure the rows for Foo
and Bar
line up.
However, it seems like this could be made safe if you took advantage of some of the internals of Selda.
On a related note, I think Selector
needs some type roles. In general, it does not appear safe to coerce
from Selector t a
to Selector u b
.
Without explicitly specifying type roles, I believe Selector
gets the roles Selector phantom phantom
, which means any sort of coercion is possible. However, I think we probably want to set the roles Selector representational representational
.
Of course, setting roles would make my trick here no longer work without unsafeCoerce
, but that is probably a good thing.
This would indeed be useful, but I'm thinking it might be a bit more user friendly to forego the intermediate Assignment
list completely and instead add a function like columnsFrom :: Row s a -> Row s b
and then use it together with with
?
In this way, your example would look something like this:
f :: m [Bar]
f = query $ do
fooRow <- select (fooTable :: Table Foo)
pure $ columnsFrom fooRow `with`
[ #c := 3
, #d := 4
]
...which I think looks a bit nicer. This would have the added bonus of making it clearer that the assignment list overrides the columns from the old row if a column is present in both. What do you think?
a bit more user friendly to forego the intermediate Assignment list completely and instead add a function like columnsFrom :: Row s a -> Row s b and then use it together with with?
That sounds like a much nicer API!
Oh, and maybe I should have created a new issue for this, but I think the more important part of this issue is about adding roles to Selector
. Without proper roles, it is possible to coerce
a Selector
to types that don't make any sense. Without proper roles, there is nothing stopping you from doing something that's not type safe.
I'd like to be able to create an
Assignment
list to pass tonew
from an existingRow
.For example, imagine I have two datatypes like the following:
As you can see, these two types are very similar.
Bar
has all the fields ofFoo
, plus a field calledc
andd
.I'd like to have a function with the following signature:
If you allow the types to be specialized, I want to use it with the following specialized type:
Essentially, I want to convert a
Foo
into a list of assignments for creating aBar
.I want to use it like the following:
I'm new to Selda, so I'm not entirely up to speed on the API, but it doesn't appear that Selda exposes enough of the internal API to allow me to write this function?
However, my guess is that Selda does have all the required information to be able to write this
assignmentsFor
function.