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

Inconsistency with record function declaration versus record function assignment #682

Open
13k opened this issue Jul 19, 2023 · 2 comments
Labels
semantics Unexpected or unsound behaviors

Comments

@13k
Copy link

13k commented Jul 19, 2023

(I don't know if I'm using correct nomenclature here with "record function", "function assignment" and "function declaration", correct if I'm wrong please).

Function declaration (function <record>.<name>(...)) seems to be stricter than function assignment (<record>.<name> = function(...)), where declarations seem to enforce arity whereas assignment doesn't.

Example (for clarity, I'm using multiple record fields):

local record R
  a: function(string, integer): string
  b: function(string, integer): string
  c: function(string, integer): string
  d: function(string, integer): string
  e: function(string, integer): string
end

local r: R = {}

----- assignment {{{

-- works as expected

r.a = function(s: string, i: integer): string
  return ""
end

-- fails as expected (wrong return type)

r.b = function(s: string, i: integer): integer
  return 1
end

-- fails as expected (wrong return type)

r.c = function()
end

-- works but shouldn't? (mismatching parameters type, correct return type)

r.d = function(s: string): string
  return "?"
end

-- works but shouldn't? (mismatching parameters type, correct return type)

r.e = function(): string
  return "?"
end

----- }}}
----- declaration {{{

-- works

function r.a(s: string, i: integer): string
  return ""
end

-- fails as expected

function r.b(s: string, i: integer): integer
  return 1
end

-- fails as expected

function r.c()
end

-- fails as expected? (if assignment behavior is correct, should work)

function r.d(s: string): string
  return "?"
end

-- fails as expected? (if assignment behavior is correct, should work)

function r.e(): string
  return "?"
end

----- }}}
❱ tl check record-function-assign-vs-declaration.tl
...
========================================
6 errors:
record-function-assign-vs-declaration.tl:21:2: in assignment: return 1: got integer, expected string
record-function-assign-vs-declaration.tl:27:7: in assignment: incompatible number of returns: got 0 (), expected 1 (string)
record-function-assign-vs-declaration.tl:53:1: type signature of 'b' does not match its declaration in R: return 1got integer, expected string
record-function-assign-vs-declaration.tl:59:1: type signature of 'c' does not match its declaration in R: different number of input arguments: got 0, expected 2
record-function-assign-vs-declaration.tl:64:1: type signature of 'd' does not match its declaration in R: different number of input arguments: got 1, expected 2
record-function-assign-vs-declaration.tl:70:1: type signature of 'e' does not match its declaration in R: different number of input arguments: got 0, expected 2

So when assigning, cases d and e pass, despite having incorrect arities. But when declaring, cases d and e fail as, I think, is expected.

This is probably related to Teal's behavior regarding function arity (both for arguments and return values: #71, #100), but I think it's a special case that could have special treatment.

So, first of all, is there any inconsistency between these two (assignment and declaration) in regards to the specific case of defining a record function?

In my opinion, having different semantics for the same thing is an inconsistency, but I may be wrong.

If so, for this specific case, when you have the record declarations available, could the assignment of a function expression be "elevated to" a function statement?

(off-topic: how come github does syntax highlighting on Teal code when viewing Teal files but won't do it in markdown code blocks?)

@13k
Copy link
Author

13k commented Jul 19, 2023

On a related note, I think there's a bug when declaring a function that is within a union type:

local record R
  a: boolean | function(string, integer): string
end

local r: R = {}

-- works as expected

r.a = function(s: string, i: integer): string
  return ""
end

-- fails but should work?

function r.a(s: string, i: integer): string
  return ""
end
type signature of 'a' does not match its declaration in R: got function(string, integer): string, expected boolean | function(string, integer): string

Maybe the type checking on declarations is too strict?

I can file a separate issue for this if needed.

@hishamhm hishamhm added the semantics Unexpected or unsound behaviors label Jul 21, 2023
@hishamhm
Copy link
Member

Record functions are pretty special because their declaration semantics are meant to accomodate some natural Lua patterns for declarations of tables and their methods. So there's a few special behaviors in place which only apply to record functions that lead to some inconsistencies.

If so, for this specific case, when you have the record declarations available, could the assignment of a function expression be "elevated to" a function statement?

This would add yet another special behavior but it might be the way to go, indeed.

I can file a separate issue for this if needed.

That would be helpful, thanks -- it looks like a separate issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
semantics Unexpected or unsound behaviors
Projects
None yet
Development

No branches or pull requests

2 participants