Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

Dumb Lowtax posted:

I feel like if you've got a good background on pointers and references from a C++ background or a quality CS intro class you just don't ever have that misunderstanding
Which is why you make a handy deep-copy function, because you know this is going to happen and you want to avoid it.

Adbot
ADBOT LOVES YOU

Nolgthorn
Jan 30, 2001

The pendulum of the mind alternates between sense and nonsense
Deep clone is an example of people using too many libraries for simple operations, in my opinion. All it takes is something like this and you get to tweak it to do whatever it is you need it to do.

JavaScript code:
function clone (input) {
    if (Array.isArray(input)) return input.map(clone);
    if (!(input instanceof Object)) return input;

    const output = {};

    for (const key of Object.keys(input)) {
        output[key] = clone(input[key]);
    }

    return output;
}

Strong Sauce
Jul 2, 2003

You know I am not really your father.





Nolgthorn posted:

Deep clone is an example of people using too many libraries for simple operations, in my opinion. All it takes is something like this and you get to tweak it to do whatever it is you need it to do.

JavaScript code:
function clone (input) {
    if (Array.isArray(input)) return input.map(clone);
    if (!(input instanceof Object)) return input;

    const output = {};

    for (const key of Object.keys(input)) {
        output[key] = clone(input[key]);
    }

    return output;
}

I mean at this point you may as well just use Object.assign since your function doesn't protect against the standard pitfalls of cloning objects.

Nolgthorn
Jan 30, 2001

The pendulum of the mind alternates between sense and nonsense
What pitfalls

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

Nolgthorn posted:

What pitfalls
Stack overflow from circular references is a good one.

Nolgthorn
Jan 30, 2001

The pendulum of the mind alternates between sense and nonsense
Alright yeah a circular reference is not handled by my function, what do you use circular references for if you don't mind me asking? JSON.stringify also doesn't handle circular references. But you can handle them with my function using a second parameter.

Nolgthorn fucked around with this message at 08:21 on Feb 3, 2019

Nolgthorn
Jan 30, 2001

The pendulum of the mind alternates between sense and nonsense
code:
// Code to be used if you're worried that the developers of NPM might start using your code

function clone (input, objects = []) {
    if (input instanceof Object) {
        if (objects.includes(input)) return '[circular reference]';
        objects.push(input);
    } else {
        return input;
    }

    if (Array.isArray(input)) return input.map(item => clone(item, objects));

    const output = {};

    for (const key of Object.keys(input)) {
        output[key] = clone(input[key], objects);
    }

    return output;
}
code:
// Or if you prefer closures

function clone (raw) {
    const objects = [];

    function performClone (input) {
        if (input instanceof Object) {
            if (objects.includes(input)) return '[circular reference]';
            objects.push(input);
        } else {
            return input;
        }

        if (Array.isArray(input)) return input.map(performClone);

        const output = {};

        for (const key of Object.keys(input)) {
            output[key] = performClone(input[key]);
        }

        return output;
    }

    return performClone(raw);
}
None of this code is tested I just typed it.

Bruegels Fuckbooks
Sep 14, 2004

Now, listen - I know the two of you are very different from each other in a lot of ways, but you have to understand that as far as Grandpa's concerned, you're both pieces of shit! Yeah. I can prove it mathematically.

Nolgthorn posted:

code:
// Code to be used if you're worried that the developers of NPM might start using your code

function clone (input, objects = []) {
    if (input instanceof Object) {
        if (objects.includes(input)) return '[circular reference]';
        objects.push(input);
    } else {
        return input;
    }

    if (Array.isArray(input)) return input.map(item => clone(item, objects));

    const output = {};

    for (const key of Object.keys(input)) {
        output[key] = clone(input[key], objects);
    }

    return output;
}
code:
// Or if you prefer closures

function clone (raw) {
    const objects = [];

    function performClone (input) {
        if (input instanceof Object) {
            if (objects.includes(input)) return '[circular reference]';
            objects.push(input);
        } else {
            return input;
        }

        if (Array.isArray(input)) return input.map(performClone);

        const output = {};

        for (const key of Object.keys(input)) {
            output[key] = performClone(input[key]);
        }

        return output;
    }

    return performClone(raw);
}
None of this code is tested I just typed it.

so why are you doing this rather than json.stringify/json parse again?

Nolgthorn
Jan 30, 2001

The pendulum of the mind alternates between sense and nonsense
Again JSON stringify throws an error on circular references.

If you generate a circular reference in your code, correct behaviour is that your application shits itself as soon as possible so that you can figure out where you did that and fix it. But someone might need that or any number of different things. The point is to write code that says what you are doing, so that when someone comes along later they don't have to guess what the point of your code is.

Probably what you are doing is more than just deep cloning the object, otherwise why would you be doing it. So while you're traversing it, perform your mutations in place. You're making a new object to serve some purpose, It's explicit.

Doom Mathematic
Sep 2, 2008

Nolgthorn posted:

What pitfalls

You code does quite surprising things when it clones anything which inherits from Object but is not a plain old Object, such as a function, date, regular expression object, boxed String or typed array. Essentially it ignores the prototype chain. It also doesn't preserve additional string properties of arrays, or symbol properties of objects. Object properties which are read-only, accessor (getter-setter), non-configurable or non-enumerable also do not have these attributes preserved.

Most of these issues can be fixed, but cloning functions is the really insurmountable issue here. Functions are essentially uncloneable because of the old "do this!" problem:

JavaScript code:
const a = {
  val: 6,
  setVal: val => {
    a.val = val
  }
}

const b = clone(a)

b.setVal(7)
Should b.setVal modify a.val, or b.val? If it modifies a.val, that's very surprising behaviour - at first glance, anyway. This question can become much more complicated in the general case. If it should modify b.val instead, how do we even construct b.setVal?

(This is kind of like touching your nose and saying to someone, "Do this!" Should they touch your nose, or touch their own nose?)

And having noted that cloning a function is something which is to be avoided, we then realise that essentially every JavaScript object has methods on it...

Anyway... it's true that JSON.parse(JSON.stringify(...)) also has problems with this kind of input (and it mishandles other, dumber things too, like undefined, Infinity, -Infinity and NaN). The point is that JSON.parse(JSON.stringify(...)) is a universally known quantity, including all of its warts, whereas for any given third-party cloning library it's anybody's guess which of these edge cases are handled well and which are not.

Roadie
Jun 30, 2013
Just use lodash.cloneDeep. It handles circular references and almost certainly does so better than anything you can come up with in a reasonable amount of time.

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
Hmm, you seem to be putting a lot of effort into this cloning code to fix up all the edge cases you run into. Maybe you should make it a library so that you only need to do it once instead of repeating it on every single project?

Strong Sauce
Jul 2, 2003

You know I am not really your father.





there is a reason why no one wants to gently caress with cloning and if they do they pray a shallow clone of their object will suffice.

Nolgthorn
Jan 30, 2001

The pendulum of the mind alternates between sense and nonsense
I maintain that you should know what's in your objects and why you are cloning them and write code that does what you want it to do.

Lumpy
Apr 26, 2002

La! La! La! Laaaa!



College Slice

Nolgthorn posted:

I maintain that you should know what's in your objects and why you are cloning them and write code that does what you want it to do.


I maintain that you should have a vague idea what's in your objects and why you are cloning them and hope the code you copied and pasted from SO does what you want it to do.

Osmosisch
Sep 9, 2007

I shall make everyone look like me! Then when they trick each other, they will say "oh that Coyote, he is the smartest one, he can even trick the great Coyote."



Grimey Drawer

Jabor posted:

Hmm, you seem to be putting a lot of effort into this cloning code to fix up all the edge cases you run into. Maybe you should make it a library so that you only need to do it once instead of repeating it on every single project?

This discussion has been such a beautiful slow burn.

Roadie
Jun 30, 2013
All this clone talk is one of the reasons that no-mutation patterns like Redux are so popular.

Lumpy
Apr 26, 2002

La! La! La! Laaaa!



College Slice

Roadie posted:

All this clone talk is one of the reasons that no-mutation patterns like Redux are so popular.

But how do you clone your objects in your reducers to return non-mutated versions?

Roadie
Jun 30, 2013

Lumpy posted:

But how do you clone your objects in your reducers to return non-mutated versions?

You don't. You use object spread, which uses the same references for non-changed values.

Suspicious Dish
Sep 24, 2011

2020 is the year of linux on the desktop, bro
Fun Shoe
allocation is sin, do not generate more GC garbage after page load.

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.



Suspicious Dish posted:

allocation is sin, do not generate more GC garbage after page load.

But if I can't post here how am I supposed to waste time at work?!

Lumpy
Apr 26, 2002

La! La! La! Laaaa!



College Slice

Roadie posted:

You don't. You use object spread, which uses the same references for non-changed values.

:thejoke:

smackfu
Jun 7, 2004

We have an internal dependency that takes a mandatory id parameter for one function. Currently, if you forget the parameter, it will error out randomly deep in the code when it calls toString on an undefined id.

What’s a typical way to handle this in JavaScript? Throw an error or console warn (or throw and warn)?

smackfu fucked around with this message at 01:13 on Feb 7, 2019

Roadie
Jun 30, 2013

smackfu posted:

We have an internal dependency that takes a mandatory id parameter for one function. Currently, if you forget the parameter, it will error out randomly deep in the code when it calls toString on an undefined id.

What’s a typical way to handle this in JavaScript? Throw an error or console warn (or throw and warn)?

Switch to Typescript so that it's impossible to forget the mandatory params.

huhu
Feb 24, 2006

Roadie posted:

Switch to Typescript so that it's impossible to forget the mandatory params.

Use Flow so you don't have to rewrite your codebase in Typescript.

Volguus
Mar 3, 2009

smackfu posted:

We have an internal dependency that takes a mandatory id parameter for one function. Currently, if you forget the parameter, it will error out randomly deep in the code when it calls toString on an undefined id.

What’s a typical way to handle this in JavaScript? Throw an error or console warn (or throw and warn)?

In any language, even those saner than JavaScript, if a parameter to a function must not have certain values, the function should fail as fast as possible and as loudly as possible. Throw exceptions, write to the console, start the fire alarm, anything you can think off. The second that function is called with incorrect values the caller must know in no uncertain terms that it is not acceptable.

Joda
Apr 24, 2010

When I'm off, I just like to really let go and have fun, y'know?

Fun Shoe
Input validation is duck typing 101. The same if you're dealing with indirection in any language. If your compiler doesn't check your types, you have to as soon as a scope receives it.

geeves
Sep 16, 2004

huhu posted:

Use Flow so you don't have to rewrite your codebase in Typescript.

Isn't Flow practically dead by JS standards? (no updates in over 6 months or something)

I found typescript more intuitive and better supported than flow but I'm also a Java / Kotlin dev and am full tilt IntelliJ which has excellent support for TS.

Dominoes
Sep 20, 2007

Does anyone else think it's nuts that both builtin Date, and moment.js (Which I believe represent the large maj of date handling) don't have separate date and time types? AFAIK, the convention is to use their datetime times for dates, times, and datetimes, which is a recipe for subtle bugs. I've been using ISO strings or tuples, processing them using one of these libs are needed, then converting back.

RobertKerans
Aug 25, 2006

There is a heppy lend
Fur, fur aw-a-a-ay.

geeves posted:

Isn't Flow practically dead by JS standards? (no updates in over 6 months or something)

I found typescript more intuitive and better supported than flow but I'm also a Java / Kotlin dev and am full tilt IntelliJ which has excellent support for TS.

Yeah, afaics it looks that way. It's not completely dead, but the lack of updates & watching bigger projects migrating away (Yarn for example) does look like nails being hammered into the coffin. It's kinda looks like FB have just lost interest, & without that backing it I don't see how it can compete

I was the other way round in terms of intuitiveness - I like the OCaml type system a lot better than TS' system. It has some nice advantages, primarily that it works with plain JS with the annotations in comments. That's a bit onerous, but if not in comments the annotations can really easily be stripped out by Babel.

I do wonder how much of the failure is to do with it not playing well with VSCode, having to disable the JS language features just to use Flow is nuts

RobertKerans fucked around with this message at 02:20 on Feb 8, 2019

Roadie
Jun 30, 2013

Dominoes posted:

Does anyone else think it's nuts that both builtin Date, and moment.js (Which I believe represent the large maj of date handling) don't have separate date and time types? AFAIK, the convention is to use their datetime times for dates, times, and datetimes, which is a recipe for subtle bugs. I've been using ISO strings or tuples, processing them using one of these libs are needed, then converting back.

If you're just saving dates and times as a string without time zones (not offsets, zoneinfo time zones like "America/Los_Angeles"), you're doing it wrong and it's going to eventually cause problems when you run into dates or times that don't actually exist (there's a bunch of them), bizarro offsets, Daylight Savings making the clock rewind, etc.

The reason these libraries are all datetimes is because what they're actually storing is a POSIX timestamp in absolute milliseconds, and everything else is just convenience methods to input/output/adjust that value. This way the systems just tick from 915148800 to 915148801 even though the wall clock time just rewound an hour because of DST changes or wall clock time just jumped forward 30 minutes because legislators decided to move to a half-hour time zone (it's a real thing).

huhu
Feb 24, 2006
Flow looks pretty active on Github. Not sure what you guys are talking about.

RobertKerans posted:

I do wonder how much of the failure is to do with it not playing well with VSCode, having to disable the JS language features just to use Flow is nuts
And it plays well with VSCode for me. We had to disable TypeScript stuff in VSCode but nothing to do with JS.

Dominoes
Sep 20, 2007

Roadie posted:

If you're just saving dates and times as a string without time zones (not offsets, zoneinfo time zones like "America/Los_Angeles"), you're doing it wrong and it's going to eventually cause problems when you run into dates or times that don't actually exist (there's a bunch of them), bizarro offsets, Daylight Savings making the clock rewind, etc.

The reason these libraries are all datetimes is because what they're actually storing is a POSIX timestamp in absolute milliseconds, and everything else is just convenience methods to input/output/adjust that value. This way the systems just tick from 915148800 to 915148801 even though the wall clock time just rewound an hour because of DST changes or wall clock time just jumped forward 30 minutes because legislators decided to move to a half-hour time zone (it's a real thing).
The bit I posted about standalone times was misleading; I'm mostly dealing with dates, which are (mostly) unaffected by TZ complications. Date/time-handling's filled with complications, but you'll find that other languages have elegant ways of handling Dates and times separately. Logically, there are cases where it makes sense to store a date or time alone, and attaching the other is obfuscating and causes problems.

Dominoes fucked around with this message at 03:53 on Feb 8, 2019

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
Okay, but what happens when your user travels from Samoa to New Zealand?

Dominoes
Sep 20, 2007

Jabor posted:

Okay, but what happens when your user travels from Samoa to New Zealand?
That depends on the use case. If you're storing info where that's relevant, I suspect a datetime's appropriate.

For example, if you're storing airplane land times, you must use a datetime. If you're setting up a series of daily schedules, standalone dates (and in some cases, times) make more sense.

Dominoes fucked around with this message at 03:55 on Feb 8, 2019

mystes
May 31, 2006

TS has more momentum now but even if everyone stops using flow that doesn't necessarily mean that Facebook will stop developing it.

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
It's relevant more often than you'd think, and you might as well always store a datetime with a timezone so that you're already prepared if it turns out you do need it.

smackfu
Jun 7, 2004

The worst is when someone converts an ISO date into a date time of midnight UTC on that date (for instance by passing it to parse), then the local time zone is negative (like US ones) so the displayed date ends up one day earlier than you started with.

Roadie
Jun 30, 2013

Dominoes posted:

If you're setting up a series of daily schedules, standalone dates (and in some cases, times) make more sense.

It only makes more sense as long as everyone who uses it never travels between time zones and never coordinates with anyone who lives in a different time zone.

Consider, for example, that "I'm open from 9 AM to 9 PM Saturday for a video conference" from somebody in London actually means "from 9 PM Saturday to 9 AM Sunday" in New Zealand.

Adbot
ADBOT LOVES YOU

smackfu
Jun 7, 2004

A good date-time library allows you to choose whether you want a local date, a local time, or an absolute date time, since there are use cases for all of them.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply