Printing a Vector before and after changing it produces unexpected results

I’m putting together a short exercise for my students on p5.Vector and its operations. The output of the code below surprises me, and I was hoping someone could explain what’s going on. I’ve been programming in Processing for years, but am new to p5.js, and so my unexpected results might have more to do with how javascript works (or how objects work, or how console output works).

Here is the code, along with comments indicating the expected results:

function setup() {
  let h = new p5.Vector(3, 4);
  // I expect both of these to show 3, 4, 0
  print(h);
  print(h.x, h.y, h.z);       

  // I expect both of these to show 21, 28, 0
  h.mult(7);
  print(h);
  print(h.x, h.y, h.z);
}

But when I run it these are the results:

Vector {x: 21, y: 28, z: 0, constructor: Object}
3 4 0
Vector {x: 21, y: 28, z: 0, constructor: Object}
21 28 0

Can someone explain why the output of the individual components produces what I expected, but the output of the Vector objects themselves does not? Processing would have produced the results I expected, but p5.js does not.

Thank you for helping me understand this!

4 Likes

Hello, @norm_krumpe, and welcome to the Processing Forum!

That is interesting, indeed!

Maybe it is a strange effect of asynchronicity in JavaScript, wherein some actions do not complete in the same order in which they are initiated. It appears that the first call to print does not receive the complete information that it needs to display until after the multiplication has taken place, while the second and fourth ones receive that information more quickly. The third one is probably also lagging, but that does not matter. Perhaps preparing information needed to print a p5.Vector is slow, and the process does not manage to fetch the contained values until after they have been changed.

In any case, in order to slow some things down, I separated some of the operations into the draw() function for some testing, here, and ran the code in the p5.js online editor:

let h;
// uncomment one of the noLoop() calls one at a time below; note the comments
function setup() {
  h = new p5.Vector(3, 4);
  // I expect both of these to show 3, 4, 0
  print(h);
  print(h.x, h.y, h.z); 
  // the following gives the unexpected results
  noLoop();
}
function draw(){
  // I expect both of these to show 21, 28, 0
  h.mult(7);
  print(h);
  print(h.x, h.y, h.z);
  // the following gives the expected results
  // noLoop(); 
}

See the comments after the calls to noLoop(). Run the code with one of those calls at a time uncommented. The two calls produce different results.

EDITED on September 6, 2021 to correct a typographical error.

3 Likes

Hi @norm_krumpe,

Welcome to the forum!

A nice thread on SO about this:

3 Likes

A nice thread indeed!

Code:

function setup() 
  {
  let h = new p5.Vector(1, 1, 1);
  
  for(let i=0; i<5; i++)
    { 
    print(h);
    //print(JSON.stringify(h));
    //console.log(JSON.stringify(h));
    //print(h.x, h.y, h.z);
    h.add(1, 1, 1);
    }
  }

Using print(h) :

Using print(JSON.stringify(h)); or console.log(JSON.stringify(h)); :

References:
https://p5js.org/reference/#/console
https://p5js.org/reference/#/p5/print

:)

2 Likes

Five customers sat down together at the Java Cafe and placed their orders. After their meal, they all walked out satisfied, each having received exactly what they ordered.

Later that day, the five customers sat down together at the JavaScript Cafe and placed their orders. After their meal, four of them walked out disgruntled, each having received exactly what the last of the five of them ordered. :grinning:

2 Likes

Was the one that stayed satisfied?

:)

1 Like

The five buddies all shared dessert wherever they ate together. This time, the one that ordered last remained and ate the entire dessert. But then there was the bill …

Now it is our job to assure @norm_krumpe that JavaScript is great, so long as we account for asynchronicity, when necessary.

1 Like

I am not going to say that JavaScript is a clean language (check this) but in this case it seems that console.log is implementation specific to the web browser console / dev tools.

1 Like

:laughing:

Well, about as clean as the words of some JavaScript users who delve deeply into the muck and those of the four disgruntled buddies who walked out of the JavaScript Cafe. :grin:

1 Like

Thank you all. Thanks to you, I learned a lot I didn’t know about JavaScript + console.log() + (asynchronous vs synchronous) + browser implementations + a JavaScript joke! And thank you, too, for the StackOverflow reference.

This will not only help me avoid some pitfalls with my students tomorrow, but also give me something interesting to talk to them about.

I wonder, then: does the p5.js print() method simply pass things off to console.log()? Could the p5.js print() be “improved” by avoiding the behavior I saw by Stringifying objects before printing them? After all, it seems that p5.js (and Processing in general) try to emphasize usability by tucking away oddities like this. “Oddities” might be too strong here, because perhaps this is all by design.

Thank you again!

2 Likes

Oh, I’m convinced! I’m teaching a course I created several years ago (an elective for CS majors highlighting the connections between mathematics and computer science), and have used Processing in it for years to look at topics such as computational geometry, image processing, fractals, random number generators, and more.

For the past couple of years I’ve toyed with the idea of switching to p5.js, mainly because sharing code and sketches is so much easier. This year, I finally made the leap, and I’m so glad I did. And I’ve enjoyed learning JavaScript along the way. I will still use Processing for teaching intro Java courses, but I will use p5.js whenever I can.

1 Like

Searching in the p5js source code for the print() function:

// src/core/environment.js

const _windowPrint = window.print;

p5.prototype.print = function(...args) {
  if (!args.length) {
    _windowPrint();
  } else {
    console.log(...args);
  }
};

So it’s passing arguments to console.log. Note that if no arguments were given to the print() function, then it’s calling the Window.print() method that prepare the current page to be printed on paper.

I totally agree with that! If people want to use the good old console.log method it’s fine and using print() with the JSON stringify trick could avoid confusion for beginner users. Maybe there’s some cases where JSON.stringify does weird things too since it’s formatting it to JSON… :wink:

1 Like

Norm, the issue you came upon in JavaScript is due to something called hoisting. JavaScript code is not linear. It has a hierarchy much like mathematical equations. There is a lot of information available on JavaScript hoisting, so I’m not leaving a link. But you should familiarize yourself with it because the more complex your code gets, the more you’ll see hoisting issues arise. This gave me more problems than anything else I learned in JavaScript. And still gives me issues. Good luck and enjoy

1 Like

A good starting point would be to look at the fundamentals on this page from MDN Web Docs:

After that, a search will reveal lots of additional information.

It looks to me that hoisting is not the problem here. From the MDN docs:

Conceptually hoisting is often presented as the interpreter “splitting variable declaration and initialization, and moving (just) the declarations to the top of the code”. This allows variables to appear in code before they are defined. Note however, that any variable initialization in the original code will not happen until the line of code is executed.

More importantly:

Declarations made using let and const are not initialized as part of hoisting.

(also see why it’s strongly advised not to use var and use const whenever you can)

In the original question, the variable is declared and initialized on the same line using let and used sequentially after that.

1 Like

A very useful discussion, thanks. I think I have seen something like this in my own debugging of p5.js stuff, but just ignored it, or rather thought “this is some glitch which will go away when I change the code a little more” and of course it did go away, as I changed my code details and debugging prints. In the Stack Overflow references there’s advice to use breakpoints and stop the code, and then debug these kinds of things, which seems good advice. I’ll try that next time.

2 Likes

This is an old discussion but a simple workaround is to copy compund data (objects and vectors) before printing them. Copying ensures that the old value is printed.

More information here:

1 Like