edgedb-python icon indicating copy to clipboard operation
edgedb-python copied to clipboard

Casts in ORM

Open vpetrovykh opened this issue 7 months ago • 7 comments

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.

vpetrovykh avatar Jun 09 '25 18:06 vpetrovykh

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?

vpetrovykh avatar Jun 09 '25 18:06 vpetrovykh

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"]

scotttrinh avatar Jun 09 '25 18:06 scotttrinh

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?

vpetrovykh avatar Jun 09 '25 18:06 vpetrovykh

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.

1st1 avatar Jun 09 '25 19:06 1st1

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!)

1st1 avatar Jun 09 '25 19:06 1st1

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.

elprans avatar Jun 09 '25 19:06 elprans

can .cast() be implemented?

1st1 avatar Jun 09 '25 19:06 1st1