Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow return type of external function to depend on typedesc parameter value #426

Closed
jclark opened this issue Feb 21, 2020 · 13 comments
Closed
Assignees
Labels
enhancement Enhancement to language design implementation/done Implementation Done lang Relates to the Ballerina language specification

Comments

@jclark
Copy link
Collaborator

jclark commented Feb 21, 2020

Because of case-insensitivity (#425), I think in the SQL connector we will need to pass the desired Ballerina type to the query method as as parameter of type typedesc, e.g.:

function query(string queryString, public sql:Value[] params = [], public typedesc<anydata> rowType = anydata) returns stream<record {}, sql:Error>;

But then we have to specify the type as both a parameter and in the cast, e.g.

stream<MyRowType,error<*>> query("SQL here", rowType=MyRowType)

which is pretty obnoxious.

I think there is a similar situation with the HTTP client.

With generic functions and the parameter to typedesc, we could do something like:

function<T is anydata> query(string queryString, public sql:Value[] params = [], public typedesc<T> rowType = anydata) returns stream<T, sql:Error>;

In this view, you have a generic function that you can instantiate with a particular type parameter to create a function that you call. Another way to think of it is that you have one function, but the result type is dependent on a parameter.

So I wonder if we could do this:

function query(string queryString, public sql:Value[] params = [], public typedesc<anydata> rowType = anydata) returns stream<rowType, sql:Error> = external;

The rowType typedesc parameter is referenced as a type descriptor in the return type. With this you would not need the cast when you call the query method.

Initially we could allow this only for external functions, so we don't have to come up with rules for what the function body of such a function would like.

We can use this to describe typedesc:constructFrom also:

public function constructFrom(typedesc<anydata> T, anydata v) returns T|error = external;
@jclark jclark added enhancement Enhancement to language design lang Relates to the Ballerina language specification status/idea An idea that is not yet a worked-out proposal labels Feb 21, 2020
@jclark jclark self-assigned this Feb 21, 2020
@jclark
Copy link
Collaborator Author

jclark commented Feb 21, 2020

@sanjiva suggests also providing a way to default a typedesc param value to the contextually expected type

@jclark
Copy link
Collaborator Author

jclark commented Feb 21, 2020

The static typing of this can work because functions are covariant in their return types, but we need a restriction that the return type uses the referenced typedesc argument covariantly. For example, if some built-in type B<T> is not covariant in T, then the return type couldn't say B<t> where t is the typedesc parameter.

@jclark
Copy link
Collaborator Author

jclark commented Feb 21, 2020

Consider:

stream<Customer,sql:Error> stm = client->query(“SELECT ...”);

where query is defined as

function query(string q, typedesc rowType) returns stream<rowType, sql:Error>;

In this case, we want to default the rowType parameter to Customer. So it’s not quite as simple as defaulting to the contextually expected type. Rather the contextually expected type gives the return type and from this return type we determine the default for the rowType.

@sanjiva
Copy link
Contributor

sanjiva commented Feb 22, 2020

Hmm that's true .. slightly more complicated. Basically the compiler has to solve a small constraint satisfaction problem to derive the value for the rowType.

@jclark
Copy link
Collaborator Author

jclark commented Feb 22, 2020

We need a syntax. Semantically it's not really the same as defaulting (where the expression specifying the default value is evaluated in the context of the definition). Best I have thought of so far:

function query(string q, typedesc rowType?) returns stream<rowType, sql:Error> = external;

@sanjiva
Copy link
Contributor

sanjiva commented Feb 22, 2020

I was thinking of something like this:

function query(string q, typedesc rowType = ?) returns stream<rowType, sql:Error> = external;

It's using the defaulting syntax but by saying "?" its saying that its not like regular defaulting .. but rather computed by the compiler.

@jclark
Copy link
Collaborator Author

jclark commented Feb 22, 2020

Only one character difference! What I don't like about that syntax is that ? is not suggestive of how it is computed. How about this?

function query(string q, typedesc rowType = <>) returns stream<rowType, sql:Error> = external;

The <> is from how a cast sets the contextually expected type; the idea is that a cast that doesn't narrow the type gives you contextually expected type.

@sanjiva
Copy link
Contributor

sanjiva commented Feb 22, 2020

Not that I don't like it, but your syntax typedesc rowType? overlaps with the optional field syntax of records and hence I find it a bit confusing to read.

I'm totally +1 for the typedesc rowType = <> version. The angle brackets make sense to me given this is about type casting in some form anyway.

@jclark
Copy link
Collaborator Author

jclark commented Feb 23, 2020

I agree: I much prefer the = <> syntax.

@jclark jclark added status/pending Design is agreed and waiting to be added and removed status/idea An idea that is not yet a worked-out proposal labels Feb 23, 2020
@jclark
Copy link
Collaborator Author

jclark commented Feb 23, 2020

Not that this is only for external functions, because the static type rules of the language cannot enforce that a function creates a value that belongs to a type specified by the value of a variable of typedesc type.

@jclark
Copy link
Collaborator Author

jclark commented Feb 23, 2020

We should change typedesc:constructFrom to use this since it more precisely describes what that does.

@jclark jclark added this to the 2020R2 milestone Mar 4, 2020
@jclark jclark changed the title Avoiding casts with data binding Allow return type of external function to depend on typedesc parameter value Mar 25, 2020
@jclark
Copy link
Collaborator Author

jclark commented Apr 21, 2020

I have separated out the defaulting part of this to #386, since that depends on #392. We can then do this issue without needing to do #392 (which is not straightforward).

@jclark jclark modified the milestones: 2020R3, 2020R2 Apr 24, 2020
@hasithaa hasithaa added the implementation/inprogress Implementation inprogress label Apr 27, 2020
@jclark
Copy link
Collaborator Author

jclark commented Apr 28, 2020

I am including in this issue applying this to the return type of value:cloneWithType (previously typedesc.constructFrom).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Enhancement to language design implementation/done Implementation Done lang Relates to the Ballerina language specification
Projects
None yet
Development

No branches or pull requests

4 participants