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

PEP: restrict automatic coercions to the any type #494

Open
hugomg opened this issue Oct 14, 2021 · 4 comments
Open

PEP: restrict automatic coercions to the any type #494

hugomg opened this issue Oct 14, 2021 · 4 comments
Labels
discussion Just for discussion

Comments

@hugomg
Copy link
Member

hugomg commented Oct 14, 2021

In many places, including variable assignments and function calls, Pallene can automatically insert a type casts to and from any. This is intended to make any more pleasant to use, without having to manually downcast or upcast everywhere.

function m.foo(xa: any, ya: any): integer
    local x: integer = xa
    local y: integer = ya
    return x + y
end

However, in addition to these automatic type casts to and from any, we also allow automatic type casts when a part of the type is any. For example, For example, automatic coercions can happen for all of the following pairs of types.

Type 1 Type 2
any integer
integer any
{integer} {any}
any->integer string->any

I'm wondering if we should be more restrictive. Automatic type casts can be confusing, and hard to reason about. Perhaps we should only have automatic casts when the source or destination type is exactly any? If we choose to restrict this, then an explicit type cast would be required for the other cases.

In theory, we could go all the way and completely remove the type consistency rule from the language. Pallene doesn't have a problem compiling inconsistent type casts, it is just that they are likely to raise a run-time exception. That said, even if we restrict the automatic coercions, I think there might be benefit in keeping the type consistency rule for the explicit type casts. It would be a way to protect against obviously bad casts. If someone really wants to do an inconsistent cast, they have the option of adding an intermediate cast to any. For example, 10 as any as string.

@hugomg hugomg added the discussion Just for discussion label Oct 14, 2021
@hugomg
Copy link
Member Author

hugomg commented Oct 15, 2021

One situation that may benefit from the current rule is a table.insert function. The argument will be a {any} but we will want to directly pass other kinds of table.

@bjornbm
Copy link
Contributor

bjornbm commented Mar 19, 2023

As mentioned in #509 (reply in thread) I am not a big fan of the implicit casting. I don't have any troubles with casting from any type (for example, integer) to any, but I am not very comfortable with the opposite. In general when casting from any a runtime error could occur and I think it is better that the programmer is forced to acknowledge this with an explicit cast.

In the example function, I think there is really no additional cost from being explicit (: integer is replaced by as integer:

function m.foo2(xa: any, ya: any): integer
    local x = xa as integer
    local y = ya as integer
    return x + y
end

Sidenote:

Arguably, if you want the implicit cast of the example function, in might opinion you might as well allow (barring shortcomings in the type inference for the polymorphic +?):

function m.foo3(xa: any, ya: any): integer
    return xa + ya
end

The return type is specified as integer, so the type of the arguments to + can also be known. Actually, I guess the restriction is with the polymorphic + since the following type checks (implicit cast when calling m.foo4 in m.foo5):

function m.foo4(x: integer, y: integer): integer
    return x + y
end

function m.foo5(xa: any, ya: any): integer
    return m.foo4(xa, ya)
end

Anyway, I think this is risky since the following function also compiles and causes a runtime error and the casting is not immediately visible (as an as integer).

function m.foo6(): integer
    return m.foo5(3.0, 'hello')
end

The Lua error message gives the line of the cast, but I still imagine it could get tricky to figure out what is going on in a more complex scenario.

In conclusion, I would prefer that pallenec told me that a cast is needed and made me explicitly put it in place (thereby calling my attention to the possible runtime error in case I overlooked it, which for me would likely be the case if I hadn't already written as …).

@hugomg
Copy link
Member Author

hugomg commented Apr 4, 2023

I feel that one argument in favor of implicit casts from any is that casting is the only allowed operation for values of type any.

@bjornbm
Copy link
Contributor

bjornbm commented Apr 6, 2023

I feel that one argument in favor of implicit casts from any is that casting is the only allowed operation for values of type any.

I would agree if the casting was guaranteed not to fail.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
discussion Just for discussion
Projects
None yet
Development

No branches or pull requests

2 participants