5 ms·
Show HN: Nothing.js – A chainable mock object which always returns itself
- _nalply 9y agoPerhaps use the hint parameter of Symbol.toPrimitive() to fix some gotchas like Nothing + 123 => '123'? See MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toPrimitive https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe... Edit: Won't help, because hint == 'number' is only triggered if used with unary plus or minus. Disregard this comment.
- slmgc 9y agoNope, hint won't help here as it gets `default` instead of `number`.
- m0meni 9y agoWouldn't this swallow errors that you wouldn't want it to swallow? e.g. you try to access a property on a value that's null (which you think isn't null), and instead of getting an error, nothing happens. Idx[0] is a way better solution to dealing with the boilerplate of checking for null/undefined that has 0 runtime cost due to being compiled away by babel. It works with type systems, and you can even use it as a babel macro[1]. With it idx(props, _ => _.user.friends[0].friends) compiles to props.user == null ? props.user : props.user.friends == null ? props.user.friends : props.user.friends[0] == null ? props.user.friends[0] : props.user.friends[0].friends Optional chaining[2] would add this to JS at the language level by using ?. as the operator. [0]: https://github.com/facebookincubator/idx https://github.com/facebookincubator/idx [1]: https://github.com/dralletje/idx.macro https://github.com/dralletje/idx.macro [2]: https://github.com/tc39/proposal-optional-chaining https://github.com/tc39/proposal-optional-chaining
- slmgc 9y agoidx requires a transpilation step. > you try to access a property on a value that's null (which you think isn't null), and instead of getting an error, nothing happens You can check for Nothing if you know you shouldn't get one. It's not about swallowing errors, it's about removing try/catch and all this `a && a.b && a.b.c && a.b.c()` boilerplate code. > Optional chaining[2] would add this to JS at the language level by using ?. as the operator But it's not there yet and it will require a transpilation step for quite some time before all the major browsers will introduce this feature. Nothing returns "safe" default values instead of null/undefined, which might come in useful in some cases. Also, it can be used in unit tests to mock some deeply-nested constructs.
- notmarkus 9y ago> The implementation uses Symbol and Proxy behind the hood so you might need to use appropriate polyfills... Does anyone know of a working Proxy polyfill? I've never been able to find one.
- JeremyBanks 9y agoIt's not possible to polyfill.
- slmgc 9y agoI've seen this one: https://github.com/GoogleChrome/proxy-polyfill https://github.com/GoogleChrome/proxy-polyfill
- sah2ed 9y ago1. You should probably add that link to your read me: The implementation uses `Symbol` and [`Proxy`](https://github.com/GoogleChrome/proxy-polyfill https://github.com/GoogleChrome/proxy-polyfill) behind the hood so you might need to use apropriate polyfills in case you want to support older browsers. 2. Personally, I find the name Nothing a bit too long. Why not use a shorter name like None?
- grincraft 9y agoThis would really benefit from adding comments in the 'how to use' code samples, or at least using standard foos and baars to make it clear what is user code and what is part of Nothing.js. For example it is not really clear if 'executeAction()' etc are part of Nothing.js. I assumed they were, but then the next sample seems to do property chain traversal transparently, without them.
- harunurhan 9y agoInteresting project, but I'd stick with idx[0] since I have transpilation setup most of the time anyway. Also some of you could be interested in optional.js[1], it's Java's Optional for js, makes it arguably more pleasant to deal with null values, no help for chaining though! [0] https://github.com/facebookincubator/idx https://github.com/facebookincubator/idx [1] https://github.com/JasonStorey/Optional.js https://github.com/JasonStorey/Optional.js
- bcherny 9y agoAnd if you’re using TypeScript, theres also TSOption https://www.npmjs.com/package/tsoption https://www.npmjs.com/package/tsoption
- vemv 9y agoRuby equivalent: class Whatever; def method_missing *_; self; end; end Sample usage: irb(main):001:0> Whatever.new.foo.bar.baz - 2 => #<Whatever:0x007fd7ed0a2a68>
- slmgc 9y agoYep, it's way easier to implement in some other languages. Btw, is it callable as well? Can you do Whatever.new().foo.bar().baz with it?
- vemv 9y ago`.foo.bar` are method calls (parenses are optional in Ruby)
- baddox 9y agoNope. Ruby doesn’t support the function call paren syntax for anything other than methods. You could do stuff like Whatever.new().foo.().bar[] though.
- djur 9y agofoo, bar, and baz are all methods here, so you can use parens.
- bcherny 9y agoThis is a really neat idea, and is a lot more ergonomic than nulls or optionals in JS. Until optional chaining makes its way into the spec [0], this is the cleanest way I’ve seen to operate on maybe-null values. [0] https://github.com/tc39/proposal-optional-chaining https://github.com/tc39/proposal-optional-chaining
- sametmax 9y agoIt's also hell to debug as it silently always works and propagate. It's good for unit testing though.
- bcherny 9y agoThat’s true, but ostensibly you only use it in places where you expect nulls and don’t intend to debug anything. That’s why Smalltalk, C#, CoffeeScript, and probably JS and TS soon have null chaining.
- _sdegutis 9y agoObjective-C also allows nil to receive messages which then just returns nil also. I’ve always found this really natural in that you can assume something is either valid or null and avoid a whole lot of null checks this way. That said, I’ve never been terribly frustrated by other languages either which throw when calling methods on null. Maybe it’s because of heavy use of certain design patterns that make it so it’s never really a surprise?
- sametmax 8y agoIt also means a chained call with an nil in it still complete from start to end. So it has additional overhead, and you better not have side effects in there. All in all, I'd like to have some kind of "?" operator in Python, I do think the pros are most of the time greater than the cons.
- Sharlin 9y agoNot a new idea, though. It’s basically the venerable Null Object pattern as documented by the Gang of Four book.
- bluepnume 9y agoOne thing I've been finding increasingly while using Flow in javascript is, null-pointer exceptions aren't really much of a problem any more. Where they might have caused a bug, 90% of the time Flow will catch them, and insist that I do a null check and handle that case. And when they do crop up, it's usually more of a serious failure that I'm happy to trigger an exception early, rather than having it propagate further down the stack by using something like nothing.js. Especially given the list of gotchas. The fact that `Nothing` is not falsy is really problematic. I'm pretty convinced at this point that `a && a.b && a.b.c && a.b.c.d` is an anti-pattern in javascript anyway; if I'm that unsure about the structure of my data, I have more serious problems than just trying to avoid null pointers.
- lojack 9y agoUsually things of form `a && a.b && a.b.c && a.b.c.d` are a sign of a law of demeter violation. This applies to more than javascript. I view code like this as a sign that there might be a better way, as opposed to a stedfast rule. Refactoring to remove these types of violations tends to lead to better code.
- grincraft 8y agoLaw of demeter applies to code, but in JS the deep property access is actually done often against data (deserialized JSON).
- daddosi 9y agoJust use try{} and keep the secret.
- TheAceOfHearts 9y agoI think this kind of access pattern is typically a code smell. Instead of carrying around arbitrarily nested values you should try to normalize it into a known and consistent shape when crossing serialization boundaries. Proxy pays a big performance penalty, and I doubt it'll be improving any time soon. In addition, I believe it's not possible to polyfill Proxy. An alternative could be to just wrap the deep property access in a try/catch: function getQux (input) { try { return input.foo.bar.baz.qux() } catch (e) { return null } }
- deleted 9y ago[deleted]
- slmgc 9y agoWrapping you code into a try/catch block decreases performance even more, you don't want to do that. You might want to check it out: https://github.com/GoogleChrome/proxy-polyfill https://github.com/GoogleChrome/proxy-polyfill
- afranchuk 9y agoYeah, my first thought when seeing that it uses Symbol and Proxy was that Proxy can't be polyfilled. But it looks like there's partial polyfills for it: https://www.google.com/url?sa=t&source=web&rct=j&url=https://github.com/GoogleChrome/proxy-polyfill&ved=2ahUKEwjVptCr5ZfaAhURjlkKHe1MB9sQFjAAegQIBxAB&usg=AOvVaw0nUHNMpcd0EoEqYO_Csr4A https://www.google.com/url?sa=t&source=web&rct=j&url=https:/...
- stareatgoats 9y agoLooks elegant, but am not going to sunset the homebaked library solution yet I don't think, because of the proxy and polyfill overhead, and also because i prefer to freely assign a specific value if nothing: TS-like syntax for clarity: Function Nothing(vTestVar:any, vIfnull:any, sObjPath?: string, bStrict?: boolean) {... return vResultOfCheck}
- deleted 9y ago[deleted]
- ollerac 9y agoIf you use Babel 7, you can install a plugin [0] to get the optional chaining syntax [1] that's currently a TC39 Stage 1 proposal! [0] https://www.npmjs.com/package/babel-plugin-transform-optional-chaining https://www.npmjs.com/package/babel-plugin-transform-optiona... [1] https://github.com/tc39/proposal-optional-chaining https://github.com/tc39/proposal-optional-chaining
- manhinli 9y agoFunnily enough, I made something similar a couple of months ago, not as a replacement for a "nothing" value but as a quick-and-dirty object placeholder and black box for tracking function calls. https://github.com/UQ-eLIPSE/kuro https://github.com/UQ-eLIPSE/kuro
- micimize 9y agoThis reminds me of my "arbitrary fluent interface" idea https://gist.github.com/micimize/064dc637103a6f3651b74380e0419bb2 https://gist.github.com/micimize/064dc637103a6f3651b74380e04...
- antoaravinth 9y agoIt looks very similar to Either functor (left which always return nothing). Looks cool though.
- hath995 8y agoI don't think I would be comfortable with production code that swallowed access errors by providing a guard post value. I feel like that would too easily slip through in places it shouldn't. To ensure it didn't happen you would need to include explicit checks everywhere, which sort of defeats the stated purpose of the library. However, it is a handy feature for writing tests. I wrote a similar library for testing called magical-mock based on the python library. https://www.npmjs.com/package/magical-mock https://www.npmjs.com/package/magical-mock
- slmgc 8y agoAdded a FAQ section: https://github.com/slmgc/Nothing#faq https://github.com/slmgc/Nothing#faq