Casts in ORM
We need to be able to express casts in the QB. It's tempting to use the already exposed types std.int64 or std.str to express casts. This may work fine for common use cases like <int64>.data. However, a big use-case for casts in EdgeQL is specifying the type that cannot be inferred:
select <array<int64>>[]
select <array<int64>>{} # different from the empty array above
select <User>{}
select <int64>{}
Another use-case is converting composite types:
select <array<str>>[1, 2, 3]
select <array<str>>Foo.int_array
select <tuple<str, float64>>(123, 456)
select <tuple<str, float64>>Foo.tuple_val
I'm not sure if we can still use the already exposed types as constructors for casts here.
Would this work?
q0 = defalt.Foo.select(str_array=lambda f: std.str(f.int_array))
q1 = defalt.Foo.select(empty_str=std.str(None))
Can we somehow make composite types work with this casting mechanism?
q1 = defalt.Foo.select(empty_str_array=std.Array(std.str, []))
# select Foo {empty_str_array := <array<str>>[]}
Or do we need a whole separate std.cast(...) function for our API?
We have a general solution for this in the TypeScript query builder: the e.cast function, which takes a constructor as the first argument and the value as the second, using the e.set() function for the empty set:
e.cast(e.array(e.int64), e.set()); // <array<int64>>{}
e.cast(e.User, e.set()); // <User>{}
e.cast(e.array(e.str), ["beep", "boop"]); // <array<str>>["beep", "boop"]
Part of this question is whether we want to copy e.cast from TS or we might have better options.
For what it's worth, I think copying the TS e.cast mechanism is fine and then the only question is which module cast should live in? Is it the reflected std or somewhere in gel.qb?
Python is more expressive than TS here, so I'm wondering if we can pull off this (idiomatic Python casting):
std.str('aaa')
std.array[std.str](...)
or if that's umbigous than this (albeit I like the above more)
std.str.cast('aaa')
std.array[std.str].cast(...)
Basically a top-level function is bad DX -- extra import; top-level cast() call kinda breaks the flow as most of the QB API is a builder pattern.
Also, I think Python QB is TS QB have diverged so much that TS can't/shouldn't influence Python QB API much at this point (design mistakes count though!)
Using "constructors" would only work if we turn std.str into a generic callable and not an actual class (for type-checking purposes). This will likely be at least somewhat gnarly.
can .cast() be implemented?