Hacker News new | past | comments | ask | show | jobs | submit login
Ruby’s Hashes and Perl’s Autovivification, in JavaScript (raganwald.com)
36 points by braythwayt on Sept 18, 2018 | hide | past | favorite | 22 comments



Great article! I had some fun with Autovivification a couple of years ago and implemented a small ruby library to do just that https://github.com/mickey/black-hole-struct.

I think the whole point was to help generate a few very long json files (http://docs.grafana.org/reference/dashboard/#dashboard-json) but in the end I had way more fun with this new concept!


I have a list of Perl ‘wtf’ moments. The discovery of autovification was one of them.

Perl autovivifies on existence checks. This has led to A LOT of bugs.

Particularly since native Perl objects have all the behaviors of hashes so instead of getting the equivalent of a null exception Perl happily creates a hash and the process won’t bomb out until I try to call a method on the hash which I was expecting as a particular type.

In legacy code, this has led to a lot of safety checks verifying the variable is actually the expected type.


Perl 5 has a problem with multilevel existence checks. Unfortunately this has been part of Perl 5 for such a long time that too many programs depend on this behaviour. Attempts at fixing this behaviour have all been reverted as far as I know.

Perl 6 does not have that problem. One can even bind an alias to non-existing keys in a hash (several levels deep) only to have vivification happen as soon as you assign to the alias.

    my %h;
    my $deep := %h<a><b><c>;
    say %h<a>:exists;        # False
    say %h<a><b>:exists;     # False
    say %h<a><b><c>:exists;  # False
    $deep = 42;
    say %h<a><b><c>:exists;  # True


In perl, you can also make hashes ‘const’ at any time, so accesses to non-existent keys will throw an error. This is also a great way to prevent dumb code typos in hash key names.


Autovivifying variables is wildly out there. I feel your pain!

Autovivifying properties of a dictionary could certaintly lead to some bugs, but as presented here, checking for existence won’t invoke autovivification.


Indeed. I wish with Perl I had a way to be explicit about when I could disable this behavior but its builtin to the language to such a degree to be unavoidable. (Using different Perl object frameworks and getter/setter methods is a possibility to protect against this class of error)

The implementation provided in the article is at least a known property of that particular object type and not a property of all objects.


You can switch off or amend autovivification behaviour using this pragma module - https://metacpan.org/pod/autovivification

    no autovivification;            # switches off autoviv except for setting

    no autovivification 'exists';   # just for exists checks


OMG. All I have to say is thank you.


If anyone's wondering how you get autoviv in Python, its surprisingly gross/elegant/fun depending on how much you use the phrasing "pythonic" in your interaction with people.

some_autoviv_variable = defaultdict(lambda: defaultdict(dict))


Cool! Does that create defaultdicts “all the way down?”


At least until it hits the turtle bedrock at the end of your system memory. Oh maybe you need the inner one to be a lambda of defaultdict actually, then it will.

     turtles = lambda: defaultdict(turtles)
     some_autoviv_variable = defaultdict(turtles)
Have to confess I'm a polyglot type person, and even Im starting to twitch at "unpythonic" feels of having something as nuts as a recursive expression in a lambda with some assignment.


> Im starting to twitch at "unpythonic" feels of having something as nuts as a recursive expression in a lambda with some assignment.

Welcome to the Dark Side. Enjoy the cookies!


I could agree with "unpythonic", as Python is a bit weak in this department, but "recursive lambda" being "nuts" is a bit much - especially here, given the site's name ;)


Oh yeah only nuts given we are in Python land. It's kind of like if some super advanced alien artefact landed on Earth. It would appear as magic incongruent with reality. Thats Python and almost anything thats expressive. God forbid your brain can imagine something beyond simple statements.


Nuts it may be, but rename it

    trie = defaultdict(lambda: defaultdict(trie))
and you have a 1-line trie data structure, which is super useful.


Not sure how it's a trie at all. Nothing is imposing prefixes here. But it is a tree sure.


> Our third use case involves checking whether the defaultValue is an ordinary value or a function. We could check every time it’s accessed, but instead we’ll assign different function bodies to the proxy’s get key. That way, it’s only checked at (open air quotes) compile time (close air quotes):

The code below that paragraph actually does check on every access, though.


Hah, yes, I made that change mid-way through to allow for Ruby’s feature where you can mutate the default value of a Hash (a feature I personally have never used), but didn’t update the text.

Fix on the way. Thanks for spotting the discrepancy!


> The code below that paragraph actually does check on every access, though.

I could be wrong, but I think a sufficiently clever JS engine (like v8) would remove that check at runtime, by realizing that half of the ternary operator is dead code. (If it's hot code and it gets optimized, anyway).


Ah, the mythical “sufficiently smart compiler” :-)


To clarify I wasn't being hypothetical, I think v8 will optimize out that check today. I phrased it vaguely because I'm not sure whether other engines would as well.


It’s certainly doable. The `defaultValue` parameter is passed by value, so it will never be rebound. The result of using `typeof` on a value never changes in JS, even if the value is modified. So... it should be possible to optimize the check away after the first time.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

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

Search: