mulesoft-labs / data-weave-rfc

RFC for the data weave language
13 stars 5 forks source link

Adding a proposal for type params args #28

Closed machaval closed 2 years ago

uip-robot-zz commented 2 years ago

Git2Gus App is installed but the .git2gus/config.json doesn't exist.

teofr commented 2 years ago

We need to think how it'll handle functions with multiple type parameters, a few options:

Also, with this in mind, do we want to statically verify that the number of arguments is the correct one? If so, we may need to introduce the parameters into the type of the functions (maybe we have this now, but also make it explicit). For example, maybe a function needs to take a parametric function:

fun f<Q, R>(g: <T>(T) -> T, a: Q, b: R): {a: Q, b: R} = {a: g(a), b: g(b)}

Notice that this is different to:

fun f<Q, R, T>(g: (T) -> T, a: Q, b: R): {a: Q, b: R} = {a: g(a), b: g(b)}

Since in the second case (I think, maybe worth discussing) T must be equal to Q and R.

Following the syntax for function application and definition, I'd say a good syntax for parametric functions types is

<ParameterBinding*>?(Params*) -> Type
TomoDC commented 2 years ago

Maybe this is out of the scope of the proposal but in the example defaultValue I would make easier the most common case, that the type String is inferred when the returning type is a lambda (avoiding literals because probably you want a lambda to be called from different values), and I would specify defaultValue<"404" | "505"> if I want to take into account literals (inferring one literal for a lambda doesn't seem useful, that could be replaced for a constant of the lambda's output)

machaval commented 2 years ago

@TomoDC I don't think inferring literals is wrong. For example you may want to do defaultValue("GET")(a.method) where a.method returns Null|HttpMethod and HttpMethod = "GET"|"POST"|... We may discuss what should be the default behaviour of the solver, but we still are going to find scenarios where we want one or the other and I think that having a way to specify solves any problem and makes it explicit

machaval commented 2 years ago

@teofr

We need to think how it'll handle functions with multiple type parameters, a few options:

All or nothing, if you have something like

fun 2List<T, Q>(a: T, b: Q): Array<T | Q> = [a, b]

you can either write 2List(1, "a") or 2List<Number, "a">(1, "a")

less or equal, with the above example, you can also write 2List<Number>(1, "a") and 2List<>(1, "a")

In my head the idea was to check that either all the Type Parameters are specified or non, so non of this sintax are going to be correct.

Just 2List(1, "a") or 2List<Number, "a">(1, "a")

Also, with this in mind, do we want to statically verify that the number of arguments is the correct one? If so, we may need to introduce the parameters into the type of the functions (maybe we have this now, but also make it explicit). For example, maybe a function needs to take a parametric function:

fun f<Q, R>(g: (T) -> T, a: Q, b: R): {a: Q, b: R} = {a: g(a), b: g(b)}

For me having type parameters declared inside a type makes noice on the visibility of that type

For example here the result of g(a)

{a: g(a), b: g(b)}
    ^^^^
     T

Is T but is not accessible inside the body as T was declared for the type only. And we make the scope global then why do we want to declare it here.

Notice that this is different to:

fun f<Q, R, T>(g: (T) -> T, a: Q, b: R): {a: Q, b: R} = {a: g(a), b: g(b)} Since in the second case (I think, maybe worth discussing) T must be equal to Q and R.

Following the syntax for function application and definition, I'd say a good syntax for parametric functions types is <ParameterBinding>?(Params) -> Type

One thing that we need to discuss is an example like

fun test<T,Q>(a: T): (Q) -> T 

In here we are requesting the developer to bind both parameters, and maybe the type of Q is yet not know. Then for me in this scenario needs to bind it to a new type parameter visible in the callsite.

teofr commented 2 years ago

I think we should at least have a decision on how function types with parametric types should be expressed, even if not implemented at first. For instance, if you want to validate that fun f<T <: String>(a: T): T = a is applied to a subtype of String, and then you have a call like f<Number>(2), I think the error message should match the error with the incompatibility between the call site and the function definition, and that's easier to make if we can say that f has type <T <: String>(T) -> T

Regarding fun test<T,Q>(a: T): (Q) -> T, I agree that if you want to use Q as another type parameter you can bind it to another parameter available on the call site, so you can make something like

fun test2<R>(a: R) -> Number = test<Number, R>(33)

But I still think this could bring problems, for example, if instead of 33 you use a random number generator.