wren-lang / wren

The Wren Programming Language. Wren is a small, fast, class-based concurrent scripting language.
http://wren.io
MIT License
6.93k stars 554 forks source link

Maps ideally should not heave member functions #724

Closed mingodad closed 4 years ago

mingodad commented 4 years ago

Hello !

In my experience maps/hashtables should not have member functions because they can conflict with keys if we allow sugar syntax for then like:

var map = {"remove": "key", "containsKey": "nop"}
System.print(map.remove)

Cheers !

mhermier commented 4 years ago

Not a problem, the language does not support this feature, and will probably never support it because allowing so opens a huge can of worms.

avivbeeri commented 4 years ago

You'd also lose access to the iterative features of a map, since the iterator protocol requires access to various properties/methods.

mingodad commented 4 years ago

LUA solves that iterator problem through a global function pair/ipair in my fork of Squirrel https://github.com/mingodad/squilu I removed member functions and replaced the with global functions, I was bitten when reading/evaluating arbitrary json/tables and then learned the lesson.

mhermier commented 4 years ago

It opens some other problems like reflection and how the compiler create symbols. So it is a big no no anyway in the current state of the language/compiler.

mingodad commented 4 years ago

One thing that I don't like about using ["string keys"] is that the compiler cannot check for misspelling and this kind of silly errors can be hard to find sometimes. One thing I did in SquiLu as an experiment is to prefix the key with '$' to indicate to the compiler to check for an already defined constant, but it's a hack.

const KEY = "oneKey";
var map = {$KEY : "value"};
print(map.$KEY);
avivbeeri commented 4 years ago

Maps in Wren don't just have string keys. You can use any built in immutable value type: Bool, String, Number, Class, Range and null, so the square-bracket syntax is good for allowing all kinds of keys.

mingodad commented 4 years ago

Ok I can see, in LUA and in SquiLu because keys can be unquoted strings that are recognized as literals it prevent using unquoted literals as variables.

mhermier commented 4 years ago

Well unless it really needs to be dynamic, you can use getters/setters instead of a map, or other startegies, like an accessors class, which takes a key in the constructor, provide a get/set method that take a map as argument.

liquidev commented 4 years ago

Maps are objects, and the dot syntax is already reserved for method calls. Wren isn't as dynamic as Lua, and does not allow for runtime modification of classes.

mingodad commented 4 years ago

Thank you for all your feedback ! The reason for this proposal is again to facilitate reuse of code from/to other languages.

mhermier commented 4 years ago

If you really need something like that, maybe lox is more suited.

mingodad commented 4 years ago

Thank you for the lox https://www.craftinginterpreters.com/the-lox-language.html reference ! Yes the spirit of reusing already known syntax code is what I'm proposing to wren because of the good performance/simplicity/documented code.

mhermier commented 4 years ago

Same original author. The C vm, while more heavily commented because of book/auto documentation, has more or less the same functionality/features. So it was an easy advise

mingodad commented 4 years ago

But the performance is not good enough the "binary_trees.lox" on my laptop takes for ever while the wren takes 0.29s

avivbeeri commented 4 years ago

The book isn't finished yet, but I imagine the final chapter, on Optimization, will help with that, once it's done.

Wren uses some smart tricks to optimise it's execution speed, so I'm hoping some of that gets documented.

mhermier commented 4 years ago

This is somehow to be expected. Prototype based languages tends to need heavy optimisations to perform near as good as statically defined classes as wren, and even more heavily to compete with fully statically typed languages.

ruby0x1 commented 4 years ago

Thanks for the comment/discussion!

As discussed, it is unlikely that map will ever add sugar for a.thing. Not at this time, anyway. For that reason I'll mark this closed but feel free to continue discussing.

To add some notes, since "the compiler cannot check for misspelling" is not a concept in this compiler, it is not directly relevant. Even with static analysis on the syntax, it can't really know what keys a map had in the first place. The map is only populated at runtime, and the compiler does not execute the code. It is information the compiler cannot have without data flow analysis stuff, and crossing function boundaries means it can't be local scope.

It seems the only answer is basically a full compiler with special data flow graph just to track map usage, which is not on the table. If it was, it would work for map["some"] all the same, because it had to be special cased, it can be told to understand this usage too.

func(map) {
  System.print(map.some) //how can it know at compile time?
}
...
var key = "so"
var map = {"%{key}me":"thing"}
func(map)