Show HN: Nothing.js – A chainable mock object which always returns itself(github.com)
github.com
Show HN: Nothing.js – A chainable mock object which always returns itself
https://github.com/slmgc/Nothing
42 comments
Usually 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.
Law of demeter applies to code, but in JS the deep property access is actually done often against data (deserialized JSON).
Just use try{} and keep the secret.
If 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-optiona...
[1] https://github.com/tc39/proposal-optional-chaining
[0] https://www.npmjs.com/package/babel-plugin-transform-optiona...
[1] https://github.com/tc39/proposal-optional-chaining
Wouldn'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
[0]: https://github.com/facebookincubator/idx [1]: https://github.com/dralletje/idx.macro [2]: https://github.com/tc39/proposal-optional-chaining
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 [1]: https://github.com/dralletje/idx.macro [2]: https://github.com/tc39/proposal-optional-chaining
idx 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.
> 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.
I 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:
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
}
}Wrapping 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
[deleted]
Yeah, 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:/...
This 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
[0] https://github.com/tc39/proposal-optional-chaining
It's also hell to debug as it silently always works and propagate.
It's good for unit testing though.
It's good for unit testing though.
That’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.
Objective-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?
It 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.
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.
Not a new idea, though. It’s basically the venerable Null Object pattern as documented by the Gang of Four book.
Perhaps 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/Refe...
Edit: Won't help, because hint == 'number' is only triggered if used with unary plus or minus. Disregard this comment.
See MDN: 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.
Nope, hint won't help here as it gets `default` instead of `number`.
Interesting 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 [1] https://github.com/JasonStorey/Optional.js
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 [1] https://github.com/JasonStorey/Optional.js
And if you’re using TypeScript, theres also TSOption https://www.npmjs.com/package/tsoption
Ruby equivalent:
class Whatever; def method_missing *_; self; end; end
Sample usage: irb(main):001:0> Whatever.new.foo.bar.baz - 2
=> #<Whatever:0x007fd7ed0a2a68>Yep, 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?
This 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.
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.
I 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
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
Looks 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:
TS-like syntax for clarity:
Function Nothing(vTestVar:any, vIfnull:any, sObjPath?: string, bStrict?: boolean) {... return vResultOfCheck}Funnily 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
This reminds me of my "arbitrary fluent interface" idea
https://gist.github.com/micimize/064dc637103a6f3651b74380e04...
It looks very similar to Either functor (left which always return nothing). Looks cool though.
Added a FAQ section: https://github.com/slmgc/Nothing#faq
> 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.
Does anyone know of a working Proxy polyfill? I've never been able to find one.
It's not possible to polyfill.
I've seen this one: https://github.com/GoogleChrome/proxy-polyfill
1. You should probably add that link to your read me:
The implementation uses `Symbol` and [`Proxy`](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?
The implementation uses `Symbol` and [`Proxy`](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?
[deleted]
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.