Hacker News new | past | comments | ask | show | jobs | submit login

Lua tables are semantically almost identical to JavaScript objects. The one key difference is that any object can be a key in a Lua table, whereas all JS object keys are coerced into strings. The other more minor difference is that you use getmetatable() and setmetatable() instead of modifying or setting object.__proto__



This is one of the things I enjoyed about Lua relative to JavaScript. Subjectively, Lua tables felt like a cleaner abstraction than JS objects.

In general, I have both of the languages filed in my head as "Decent scripting languages with the risks associated with any-typed variables and soft coercion", but given those risks, I prefer Lua because it's simpler; there are just fewer abstractions and corner cases to trip over. Contrasting with JavaScript, where the sheer scope and complexity of the language (which has only grown as features have been added to try and avoid the sharp edges, which unfortunately only makes the sharp edges into legacy sharp edges, it doesn't eliminate them) means there are an uncounted number of ways to shoot yourself in the foot (https://www.destroyallsoftware.com/talks/wat).


I've been a fan of that talk since 2012, and I agree that JS is full of bad decisions (but hindsight's 2020). But I use JS every day for work and probably the only gotcha I actually run into is due to hoisting, which my linter (tslint) catches for me within my IDE (VS Code). Meanwhile all the new features are actually letting me code more quickly and easily, so that writing Lua now feels archaic and frustrating. Destructuring for example is a great way to emulate multiple return values, and it's more generally useful than just multiple returns too (used in parameter lists, etc). I do hope for the day backwards compatibility in JS is broken and we get a cleaner, lighter, more portable, more embeddable spec and implementation. I don't think we'll see that for another 15 years at this rate. But it'll probably arrive one day.


If your linter is TSLint, aren't you using TypeScript?

That's actually my solution. I don't code directly in JS anymore; I always approach it from TypeScript. I do not have time to track down bugs resulting from typos (or brain burps) causing me to mis-assign values to the wrong variables, and static typechecking coupled with variables with immutable type specifications eliminates about 80% of those instances for me.

(Going that road, backwards compatibility in JS isn't a concern for me as a web app developer, because it's pushed into the realm of "Things compiler and browser writers care about." ;) )


Yep, I use TypeScript for the exact same reasons. Although I've been bitten a couple times by its unsoundness (specifically with mismatched function arity in an interface) and looked briefly at ReasonML as a possible alternative. But that's more of a long term hope. TypeScript is good enough for 99% of the things I need, and great for web dev. That said, semantically it's still JS, and it still has the same flaws, like hoisting (or something having to do with const and let not being what I think they are), which still bites me every once in a while.


> Hindsight is 2020

Until right now, it never occurred to me to read this as a year. Would be a great campaign slogan.


It's 20/20 and is used in ophtalmology to express dioptry: 10/10 is perfect vision and 20/20 is elven or hawk eye sharp, beyond human eyes' capability.


Henry Hindsight 2020 - Make the Web great again.


Couldn't this be accomplished with something like the "use strict", telling it to use the new version instead?

That way, the existing web doesn't break, and people can start using a more sane JS as support rolls out over the course of 1-2 years.


Yes but it would need to be highly coordinated by a bunch of competing parties with opposing interests who are probably only going to agree on anything when the status quo hits a serious breaking point and not a second before that. Look at the ES4 disaster and original Dart situation, any hypothetical breaking evolution of JS would have to overcome similar challenges.


> The one key difference is that any object can be a key in a Lua table

How does this work? Internally it is implemented using a hashmap so somehow they must generate a hash value of the object? What if the object has member variables that are themselves objects? What if those things point to each other in a cycle?


In that case the object's is hashed by identity (location in memory). That's how Javascript's Map (separate from a bare object) works for instance:

    > let m = new Map
    > let o = {}
    > m.set({}, 1)
    Map { {} => 1 }
    > m.set(o, 2)
    Map { {} => 1, {} => 2 }
    > m.get({})
    undefined
    > m.get(o)
    2
or Python's dicts:

    >>> class A: pass
    ... 
    >>> a1 = A()
    >>> a2 = A()
    >>> {a1: 1, a2: 2, A(): 3}
    >>> d = {a1: 1, a2: 2, A(): 3}
    >>> d
    {<__main__.A object at 0x107a9f1d0>: 1, <__main__.A object at 0x107a9f208>: 2, <__main__.A object at 0x107a9f2b0>: 3}
    >>> d[A()]
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    KeyError: <__main__.A object at 0x107a9f240>
    >>> d[a1]
    1
    >>> d[a2]
    2
or Ruby's:

    > class A; end
    > a1 = A.new
    > a2 = A.new
    > m = { a1 => 1, a2 => 2, A.new => 3}
    => {#<A:0x007fbe691ecdf8>=>1, #<A:0x007fbe691f4f08>=>2, #<A:0x007fbe6920efc0>=>3}
    > m[A.new]
    => nil
    > m[a1]
    => 1
    > m[a2]
    => 2
Statically typed languages can more easily constrain keys[0], but even then the lure of all objects being hashable and equatable is strong e.g. I'm pretty sure all Java and C# objects are hashable and equatable (and can be used as hashmap keys out of the box).

[0] Rust's hashmap requires that its keys implement Hash and Eq, neither of which are implemented by default; Haskell's Data.Map that its key be Ord; ...


Equivalence is by whether that name refers to the same object (like “pointers” in C).


They're identical to dictionaries/hash tables in pretty much any language, unless I'm missing something. Besides C, every language I've worked in comes with one of these things built-in. It's surprising to me that so many people consider this noteworthy today. Would someone please enlighten me as to why it is?


Because `.foo` means `[‘foo’]` and because you can easily make sequences out of them (if you write `{ ... i1 = v1, v2, ... }` then `v2` automatically gets the “next” natural number as a key) the ergonomics make them usable as structures and arrays easily. Also the ‘:foo()` syntax binds the LHS of the operator as the first parameter for a method call, and metamethods allow you to easily implement inheritance / dispatch / etc.

It’s more about ergonomics than availability.


Not a full equivalence, but this[0] gets you x.foo, x.bar ergonomics as opposed to x['foo'] etc.

[0] https://docs.python.org/3/library/types.html#types.SimpleNam...


> `.foo` means `['foo']`

Ruby does the same, except that it's `[:'foo']` because Ruby has two string types because fuck you.


>Besides C, every language I've worked in comes with one of these things built-in.

JavaScript? When talking about a language most refer to as "JavaScript-like", it's definitely a noteworthy feature. Otherwise, you're right, it's pretty much expected to be available. Although, I'd say beginner developers might forget about it since the typical use case has string or number keys.


In both languages objects double as arrays, although they do this slightly differently. But the differences are mostly superficial.


PHP also does that, yet doesn't use its arrays for metaprogramming & object orientation (using a separate object system instead).


> Would someone please enlighten me as to why it is?

Both Lua and Javascript attach protocols to their hashmaps which most languages attach to other bits (classes, typeclasses, traits, …) — even if those other bits are underlaid by hashmaps at the end of the day.

They're more like general-purpose objects which can act as hashmaps (badly in the case of javascript).


one can "overload" operations on table instances, most importantly, accessors by forwarding them to another table, which is somewhat similar to prototype-based inheritance


I don't think it's just similar, I think it actually is prototypical.


Aren't Lua tables simply like JavaScript Maps?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: