erde-lang / erde

A programming language that compiles to Lua.
https://erde-lang.github.io
MIT License
39 stars 4 forks source link

Arrow function ambiguity + require `global` keyword for declarations #7

Closed tp86 closed 1 year ago

tp86 commented 1 year ago

I read documentation about arrow functions and noticed that there seems to be no way to return table from single-expression arrow function. Below is slightly modified example from documentation:

-- Return single value
local getRandomNumber = () -> { value = math.random() }

What would happen in this case?

  1. Will erde treat it as arrow function with body creating global variable value? (some suggestion: maybe global keyword should be required to create global variables - it is recommended anyway)
  2. Will erde treat it as arrow function returning table with value as key?

Are there any rules for above case?

BTW, this project looks very interesting and promising!

tp86 commented 1 year ago

I've just looked at specs and played a bit in playground and noticed that you need to wrap table in parens. So above example becomes

-- Return single value
local getRandomNumber = () -> ({ value = math.random() })

Without parens calling function will create global variable. I find it a little bit error-prone: one can easily forget to wrap table in parens and get unexpected behavior polluting global namespace. Hence my suggestion to require global keyword. Making functions local by default is already different than in lua. Approach with functions local and other variables global by default is confusing, which making global required would solve as well.

bsuth commented 1 year ago

BTW, this project looks very interesting and promising!

Thanks for your interest! I'm glad someone else has taken the time to try it out!

I've just looked at specs and played a bit in playground and noticed that you need to wrap table in parens.

That's right, otherwise the syntax is ambiguous and its impossible to tell whether the user wants an assignment or to return a table. This is admittedly not so obvious, so I'll add a note in the documentation.

some suggestion: maybe global keyword should be required to create global variables - it is recommended anyway

I actually originally wanted to make the global keyword required, but it's not so straightforward to implement and requires some extra effort from the user. For example, global variables will need to be forward declared before usage:

-- at the top of the file
global my_global_var

-- use later on
print(my_global_var)

This is because each file is compiled separately without any knowledge from other files (and I think this is an important behavior to keep). However, seeing as global variables are usually mistakes, I think this is fine.

Since the majority of the compiler is done and has been relatively stable, I'm also okay with taking a stab at enforcing the global keyword. It may not be done soon, but I agree that it would probably be a better feature to have than not have :eyes:.

tp86 commented 1 year ago

Returning table inside parens is IMHO ok to make it non-ambiguous. For example, Python requires comma in single-item tuple (item,). Adding this case to documentation would definitely make things clear.

Regarding global, I agree that globals would have to be forward declared. But maybe it should be like in Fennel - make global access explicit using _G.global_var and use something like --allowedglobals compiler flag for globals that are builtin or provided by host environment.

Explicit global declaration is just an idea. Personally, I'm ok with global being default just as it is in Lua - it has never been a problem for me.

But it doesn't work nice with changed default scope for functions. One coming from Lua would expect that function f () end is the same as f = function() end and it's not the case - former declares local and latter global function (if I understand correctly). I have just been thinking in Lua :man_facepalming: and realized that Erde doesn't allow anonymous functions in the form function() { } (not to mention function f () end is not valid Erde either...)

bsuth commented 1 year ago

made an update to the docs regarding arrow function ambiguity here: https://github.com/erde-lang/erde-lang.github.io/commit/748bf596313cd2ece68cc4fc26defb10c17eddaf

bsuth commented 1 year ago

But maybe it should be like in Fennel - make global access explicit using _G.global_var and use something like --allowedglobals compiler flag for globals that are builtin or provided by host environment.

Personally im not a huge fan of this approach. I still want globals to feel like globals in Lua, but I definitely do also feel the awkward inconsistency here.

I would also consider removing the behavior of making functions local by default, since it may be going against the expected behavior for the majority of users. It was originally added since I thought it was very common for people to forget to declare functions in Lua as local, but it may be more important to simply be consistent with Lua here.

tp86 commented 1 year ago

I think it's fine to have functions local by default - this is minor inconsistency that is well documented. I forgot local before top-level function more often than forgetting local before variable declaration.

My biggest concern was that assigning variable to anonymous function would be different than in Lua, but it was before I realized that anonymous functions in Erde are already different than in Lua.

In summary, I'm fine with current approach to globals and locals. In future we can come back to requiring global in declarations if it causes issues for users.

bsuth commented 1 year ago

Sure, I'm totally fine with revisiting this one after some more opinions are gathered. In that case, I'll keep this issue open for others as well

bsuth commented 1 year ago

I've thought about this one a lot and decided that I want to remove having local functions by default, as it simply differs from Lua's behavior too much and goes against the "Close mapping to Lua" principle of Erde. I have implemented this change in e006df3ba164fb8f6e8875b6a46fe14175a47ef0 so I think we can close this one now :partying_face: