Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Style guide questions #6607

Open
calebfoss opened this issue Dec 3, 2023 · 28 comments
Open

Style guide questions #6607

calebfoss opened this issue Dec 3, 2023 · 28 comments

Comments

@calebfoss
Copy link
Contributor

Topic

As we're working on example revisions, I had a few questions about the code style guide.

  1. Using single quotes for strings: This means that if a contributor is writing their code in the p5 editor and presses the Tidy Code button, they are breaking the style guide. This is true of any editor using Prettier. On my workspace, I customized Prettier to use single quotes so that I could still use it. Since there is no explanation included, I wanted to ask the reasoning for this rule. If we're sticking with it, I'd recommend the p5 editor's Tidy Code feature be modified to fit and perhaps instructions in the style guide on how to customize Prettier on other editors.
  2. Don't use for loops for iterating over arrays: This requires us to use syntax that I think is much harder to read and less accessible for folks new to programming. I would strongly advocate for using for... of loops for arrays. I would love for creators to feel able to try out iteration without learning about arrow functions, array methods, and passing anonymous functions into methods. Otherwise, we're adding a ton of steepness to the learning curve.

Tagging @nickmcintyre and @raclim for thoughts

@limzykenneth
Copy link
Member

  1. No particular reason that single quote is chosen since there won't be any. The editor should be able to be configured to use single quote instead of double quote for tidy code. An issue can be filed there.
  2. The style guide recommends .forEach and not for loops. Is what you mean here being that to use for...of instead of .forEach?

@nickmcintyre
Copy link
Member

  1. Good point. I actually made the same suggestion to match the p5.js Web Editor's style. My understanding is that Prettier was removed as a dev dependency for performance reasons. We stuck with a modified version of the Airbnb JavaScript guide, which uses single quotes. I agree we should aim for consistency.

  2. I disagree about readability, but I hear your point about smoothing out the learning curve. How about we linger here for a moment? Here are some code snippets to compare different iteration scenarios:

Simple iteration

for (let mover of movers) {
  mover.update();
  mover.render();
}
movers.forEach((mover) => {
  mover.update();
  mover.render();
});

Using an index

for (let i = 0; i < movers.length; i += 1) {
  let x = i * 10;
  let y = 20 * sin(x * 0.01) + 200;
  movers[i].pos(x, y);
  movers[i].update();
  movers[i].render();
}
movers.forEach((mover, i) => {
  let x = i * 10;
  let y = 20 * sin(x * 0.01) + 200;
  mover.pos(x, y);
  mover.update();
  mover.render();
});

Updating

for (let i = 0; i < sizes.length; i += 1) {
  sizes[i] += 1;
}
sizes = sizes.map((size) => size + 1);

Everyone's learning path is different, but it's probably safe to assume that someone programming with arrays could pick up .forEach() pretty quickly. Our intro chapter will teach arrays after functions, basic OOP, and loops. .forEach() and other array methods fit pretty naturally into that scope and sequence.

All that said, it seems like @calebfoss suggested a more general principle of using syntax with the fewest prerequisites (please feel free to correct me). @katlich112358 @dkessner @MsQCompSci @limzykenneth @davepagurek @Qianqianye what do y'all think about array methods? Are there any other opportunities to simplify the the code style guide?

@limzykenneth
Copy link
Member

The point about having the index is interesting. With a for...of loop, the index does not come directly but it is possible for the user to use arr.entries() with a full for (const [i, val] of arr.entries()) although in this case the user will need to know about .entries() and array desctructuring for [i, val] which it is worth thinking about how it compares with .forEach((val, i) => {}).

On the other hand, for...of have a couple possible advantages over .forEach in that 1. you can break or continue; 2. await will work as expected.

@davepagurek
Copy link
Contributor

I don't have a super strong opinion on this one, both .forEach or for..of have their advantages and I'm happy to use either in examples.

One minor bit to add to the discussion: I'm not sure about everyone else, but even though I know the difference between for..in and for..of, I find it harder sometimes to spot one vs the other when reading code, as both read very similarly in English. I'm not sure if we have any examples that loop over both object keys and arrays, but in that case, Object.keys(array).forEach(...) is more verbose but maybe clearer to show that something different is happening.

@calebfoss
Copy link
Contributor Author

calebfoss commented Dec 4, 2023

Thank you for the discussion! I appreciate y'all taking the time.

I wanted to clarify a couple things. I personally really like using array methods and find them quite readable if written well, but speaking as an instructor these are the aspects of using array method that would present obstacles for my students:

  1. => - This is the first time students would see this symbol. Previously I have not taught lambda expressions / arrow functions at all, so it would be the only place they would encounter it. In the p5 Examples, iterating over arrays would be the only place they would be used.
  2. implicit return value - By the time my students would be iterating over arrays, they would have gotten started with writing their own functions, but they would only be using them to draw custom shapes. So they would not only need to learn about returning values from custom functions but also the way the arrow functions can implicitly return values.
  3. passing a function into a function - I've found this is tough for beginners to conceptualize, especially since even those with experience in other languages may not have dealt with this. That being said, they would also deal with this in p5.Element event methods like mousePressed.

My students would have already learned about for loops, so that syntax structure would be familiar. Honestly, if the p5 code examples use array methods, as an instructor, I would most likely show my students a modified version that uses the tools they've already learned.

I wrote a little add-on with a Python-esque range function to help students with numerical iteration. It could also be used for updating array values, since for... of doesn't allow for that:

for(let index of range(sizes.length)) {
    sizes[index] += 1;
}

I was thinking about proposing that function for the base p5 library, but I'll put that in a new thread. A big benefit is that infinite loops are impossible, which is a huge plus for beginners.

@davepagurek Good point about the "in" vs "of" confusion. That is a notable downside.

@limzykenneth Good point about break, continue, and await. One annoying thing about iterating over the entries method is that the indices are strings. It's not a huge issue, but something that caught me off guard that I wanted to mention.

Hearing the pros and cons for different options makes me wonder - maybe the style guide could include multiple and suggest different ways of iterating for different situations? Using only array methods would be more consistent, and if we're sticking with that, I'll make sure all the relevant examples use them. Again, I would just work around that when I'm teaching.

@davepagurek
Copy link
Contributor

One annoying thing about iterating over the entries method is that the indices are strings

I just did a quick test, it looks like they're numbers here at least?

image

Iterating over the indices of an array with in definitely makes strings though:
image

@calebfoss
Copy link
Contributor Author

One annoying thing about iterating over the entries method is that the indices are strings

I just did a quick test, it looks like they're numbers here at least?

image

Iterating over the indices of an array with in definitely makes strings though:
image

Oops! Thanks for checking.

@calebfoss
Copy link
Contributor Author

Also just to contradict myself a bit and confuse things - the same thing I said about => being a new symbol would be true about the keyword "of". My opinion is that using "of" is much simpler to learn, and the syntax involved is much simpler. As an English speaker, though, I'm biased towards English keywords over abstract symbols.

@calebfoss
Copy link
Contributor Author

calebfoss commented Dec 4, 2023

A more verbose approach that would avoid arrow functions would be:

function addOne(size, index) {
  sizes[index] = size + 1;
}

sizes.forEach(addOne);

@limzykenneth
Copy link
Member

function addOne(size, index) {
  sizes[index] = size + 1;
}

sizes.forEach(addOne);

Just a quick note that this style is also what I see Dan Shiffman do in the beginner oriented videos for Coding Train, albeit for event handlers, so it's not out of the question.

@calebfoss
Copy link
Contributor Author

function addOne(size, index) {
  sizes[index] = size + 1;
}

sizes.forEach(addOne);

Since it doesn't look like this goes against the style guide as-is, I'm thinking this would be a good format to use in the examples. We have already examples with named functions being passed into DOM element event methods, so this would be more familiar. If anyone sees any issues with that, please let me know. If not, it might be worth including this style in the style guide as an alternative.

@davepagurek
Copy link
Contributor

I think this is OK as long as we keep the function and its use side-by-side like in these snippets so that people can still read the code linearly without jumping around too much, as opposed to placing them next to globals like draw.

@limzykenneth
Copy link
Member

limzykenneth commented Dec 5, 2023

For me it is still a bit hard to decide which to go for. In everyday coding I instinctively use .forEach with inline closure just because I personally like the syntax but when later I found out I need to await, I'll be converting it into for...of. I don't know really is the main takeaway.

One thing to note also is that we might do some edits to the style guide later on to clarify differences between example code and p5.js source code style, ie. example code style are designed to be consistent, simple, and beginner friendly; while the p5.js source code style may have more in the ways of case by case analysis, small tricks, and optimizations when necessary. One example is const and let, example code only uses let while p5.js source code uses const whenever possible and only use `let if reassignment is needed.

Forgot to add,

sizes.forEach(function(size){
  size = size + 1;
});

is also possible if the desire is to avoid arrow syntax, as long as this loop is not used in object methods.

@calebfoss
Copy link
Contributor Author

That's a great idea to distinguish between examples and source code. For examples, I think beginners would find it easier to read with the function declared outside the method call, which is why I wrote it that way, even though I'd personally never write code that way. In my experience, the more nested parentheses and braces, the more students/beginners get stuck on syntax issues that hold them back from doing what they want.

@nickmcintyre
Copy link
Member

nickmcintyre commented Dec 5, 2023

In my experience, the more nested parentheses and braces, the more students/beginners get stuck on syntax issues that hold them back from doing what they want.

I think that's the essential point. Arrow functions are dope, but they're definitely an intermediate concept with enough nuance to trip people up. I also agree that most beginners and their instructors are more comfortable with some variation of a for loop.

What do y'all think about the following?

Array iteration

// Updating.
for (let i = 0; i < sizes.length; i += 1) {
  sizes[i] += 1;
}
// Not updating.
for (let size of sizes) {
  circle(200, 200, size);
}

Callbacks

// Bad.
button.mouseClicked(() => {
  thingOne();
  thingTwo();
});


// Good.
button.mouseClicked(handleClick);

function handleClick() {
  thingOne();
  thingTwo();
}

@calebfoss
Copy link
Contributor Author

@nickmcintyre I'm in favor of that of course. I must admit that I went into this discussion only thinking about documentation and not source code. I don't see any issues with using arrow functions passed into array methods in source code. The only one I might be cautious of is reduce, which can become super confusing to read if not written carefully. If we're sticking with one style guide for both source code and documentation though (at least for now), what you have there looks great to me.

@nickmcintyre
Copy link
Member

Oops. To clarify, my style suggestions are focused on documentation.

I don't actively contribute to the p5.js source code (yet), so I don't really have an opinion there. There's probably an upside to adhering to something standard-ish such as Airbnb for most situations.

@GregStanton
Copy link
Collaborator

GregStanton commented Dec 20, 2023

Great discussion! Somehow I missed this issue but found it when @nickmcintyre mentioned it in #6527. I think there's a case to be made for using only traditional for loops in the p5.js reference examples. I'll tread lightly since I seem to be a minority of one here, but I think there's a solid case to be made for this approach! (Sorry in advance that this answer is so long. I tried to edit it down I swear!)

Reasons to use for in all cases

Using only for loops reduces the prerequisite knowledge required to understand the documentation. In particular, it means beginner's don't need to learn more than what's in the Foundation section of the p5.js reference, which contains for but not for...of or forEach(). I think a good rule of thumb may be to write code examples that leverage only the language features included in the reference, when possible, since p5.js is often used as a first introduction to programming. (I'm cheating slightly here, since the .length property of arrays doesn't seem to be in the reference, but I think that's less of a reach for beginners.)

It reduces prerequisite knowledge because beginners already need to know for loops to iterate in general (not just over arrays). We can see this reflected in the style guide, since the section on iteration opens by saying "Use for loops to iterate a fixed number of times." If the style guide's section on iteration simply ends at "Use for loops," it becomes simpler for the contributors following the guide and for the beginners reading a reference that follows the guide. We could still add a remark for source-code contributors about more nuanced guidelines.

While it may seem like a small step to learn .forEach() or for...of, I think it's important to not underestimate the curse of knowledge. As a concrete example, experts may feel callback functions are easy to explain, but they can be hard for beginners and they're not necessary for iterating over arrays. I think they're okay for the event handling portion of the p5 API because in that case they're harder to avoid, and because that's a more advanced topic.

Verbose options can actually be easier for beginners

After many years of tutoring math (and some programming), I still need to remind myself to be careful about the curse of knowledge. Instinctively, I still feel like I can explain a concept in a few minutes, even though in reality, it usually takes ten times as long.

I'll give an example from my most recent tutoring session. We were working on a math problem, but the situation was nearly identical to the programming problem we're discussing now, which is to figure out how to iterate over an array. I identified three approaches for the solution. Among them, one solution was by far the fastest, simplest, and best suited to the problem. But the student found it difficult to implement because it was new. They had an easier time with a longer, indirect approach based on something they had already learned. This isn't actually surprising because beginners need to do a lot of hard things in order to use a new technique:

  1. Recognize there is a different approach from the one they first learned (e.g. .forEach instead of a for loop).
  2. Learn to implement the individual details of the new approach.
  3. Mentally chunk the details, associating them with a single big idea they can remember.
  4. Develop associations in order to identify when each approach is most useful.

Without chunking, a beginner's fragile working memory can be exhausted by the details of a single approach, making it hard to decide between multiple approaches (like for vs. for...of). All of this is especially difficult in the beginning because they might still be shaky on the first approach they learned; introducing new approaches too early may exacerbate this since they may get insufficient practice with the first approach.

Because of these kinds of issues, I tend to favor an emphasis on the irreducible minimum of core concepts: variables, basic data types and structures, control flow (if...else and for), and functions (not including arrow functions). It's possible to get very far with just these ideas, and I suspect that using the same syntax consistently will prevent certain misunderstandings. Beginners tend to focus on surface features, like syntax, rather than deep structure, like iteration.

Points in favor of forEach() or for...of for iterating over arrays (with counterpoints)

  1. Pure functions
    a. Point: The current style guide offers a short explanation in favor of forEach() and related methods: "Pure functions are easier to reason about than side effects."
    b. Counterpoint: But forEach() accepts callback functions, and functions can indeed have side effects. Pure functions don't have side effects (by definition), but there's nothing to keep a beginner from passing in a function with side effects. And if we use arrow syntax, we're increasing the prerequisite knowledge even more.
  2. Verbosity
    a. Point: These options can be less verbose.
    b. Counterpoint: We'd probably need to use named functions to make forEach() friendlier for beginners, and then this advantage is diminished in that case. In any case, the more verbose option may actually be preferred by beginners (as I mentioned above), so I'd err on the side of for, since at least it reduces prerequisite knowledge.
  3. Off-by-one errors
    a. Point: There's no risk of off-by-one errors with for...of loops, since they're less imperative than for loops (they don't require the user to give direct instructions on how to handle the index).
    b. Counterpoint: This a fair point, but intuitively, it seems like less of an obstacle than learning new syntax.
  4. Exposure to new features
    a. Point: Using forEach() or for...of introduces beginners to new language features.
    b. Counterpoint: It's true that at a certain point, learners will benefit from this exposure, even if it's just so they can read code written by others. My feeling is that the p5.js reference examples just aren't the right place for this.
  5. Modern practices
    a. Point: In the context of array iteration, these options are more modern.
    b. Counterpoint: I don't think this is a sufficient reason by itself. People still use for loops for other things, so the syntax won't be deprecated or anything. And this would be a deliberate choice in order to reduce knowledge barriers.
  6. Confusion among experienced users
    a. Point: These options may reduce confusion from more experienced users, who otherwise would want to know why forEach() or for...of aren't used.
    b. Counterpoint: Experienced users will understand the code either way. If we use forEach() and for...of, beginners may actually get stuck.

Overall, I think a big part of what makes p5.js amazing is that it gives beginners an on-ramp that's not too steep. The p5.js reference is a major piece of that onramp, so making that piece steeper requires a good justification. But I'm open to persuasion.

Edit: Changed the headings for clarity, and removed some redundant language.

@lindapaiste
Copy link
Contributor

lindapaiste commented Dec 20, 2023

I think that there should be different style guidelines for source code vs. examples. Examples should use the most simple and beginner-friendly code, while the library source can use more "cutting edge" stuff.

The point about having the index is interesting. With a for...of loop, the index does not come directly but it is possible for the user to use arr.entries() with a full for (const [i, val] of arr.entries()) although in this case the user will need to know about .entries() and array desctructuring for [i, val] which it is worth thinking about how it compares with .forEach((val, i) => {}).

Of those two I have a strong preference for forEach over for (const [i, val] of arr.entries()). I think the destructuring is too confusing. But I think that the best approach for example codes is to use a basic for (let i = 0 loop and access arr[i] within the loop.

On the other hand, for...of have a couple possible advantages over .forEach in that 1. you can break or continue; 2. await will work as expected.

I very rarely use break and continue when I'm writing code. Newer JS array methods like .some() and .every() can be used in most cases. IMO we should definitely use these more specific array methods in source code when possible. Probably not in examples though? They are easier to read than an i loop because it's more like writing a sentence in English. if (arr.some(isWhatever)) is easy to understand. However it would be introducing a beginner to new methods that they may not have seen before.

await in loops in inherently confusing because there's the distinction of whether you want to run in series or in parallel. no-await-in-loop is a good eslint rule. Most of the time it should be in parallel and should be await Promise.all(arr.map(doSomethingAsync)), where we are mapping each element to the Promise returned by an async function, but that is not at all beginner-friendly.

I don't have a super strong opinion on this one, both .forEach or for..of have their advantages and I'm happy to use either in examples.

One minor bit to add to the discussion: I'm not sure about everyone else, but even though I know the difference between for..in and for..of, I find it harder sometimes to spot one vs the other when reading code, as both read very similarly in English. I'm not sure if we have any examples that loop over both object keys and arrays, but in that case, Object.keys(array).forEach(...) is more verbose but maybe clearer to show that something different is happening.

I agree that the difference between for..in and for..of is extremely confusing and I think it's too much for a beginner.

@lindapaiste
Copy link
Contributor

lindapaiste commented Dec 20, 2023

  • Don't use an else block after an if block that always executes a return statement.
// Bad.
function mouseIsOnLeft() {
 if (mouseX < width * 0.5) {
   return true;
 } else {
   return false;
 }
}

// Good.
function mouseIsOnLeft() {
 if (mouseX < width * 0.5) {
   return true;
 }

 return false;
}

I disagree with this rule in the current documentation style guide. Since we are aiming for clarity and beginner-friendliness, I think that the explicit else makes the code easier to read and understand.

For the record, we use the unnecessary else a lot in the p5.js source code. We have 71 violations of eslint rule no-else-return if we were to enable that rule.

@calebfoss
Copy link
Contributor Author

calebfoss commented Dec 20, 2023

I really appreciate this discussion! It's great to see a range of perspectives all working to make our documentation that uses iteration more accessible. This a great reminder that there is no universal concept of what is beginner-friendly. As @nickmcintyre said early on

Everyone's learning path is different

To be a bit more open about the origins of my position, let me write a bit about my own learning path: One of my first introductions to code was JS expressions in After Effects. I then moved on to JS for Unity (back when that was a thing), Processing, and Arduino as my first environments for writing programs.

By the time I started using Python, the way it handled iteration confused me. I was so used to a 3 statement for loop, I didn't understand why that was no longer an option.

From Processing Python reference

loadPixels()
for i in range((width*height/2)-width/2): 
    pixels[i] = pink
updatePixels()

Now reflecting years later, I wished I had first learned to iterate using the Python structure. I find the syntax quite a bit simpler, but more importantly, it removes the risk of infinite loops. I could have had such an easier time experimenting and making mistakes with less severe consequences.

The only reason Python's way of iterating seemed strange and confusing was that I had learned a 3 statement for loop first.

Part of my point here is that I want to question the idea that a 3 statement for loop is the most basic approach. It opens up the risk of infinite loops, and it squeezes 3 statements into 1 line. It feels basic to me personally because I learned it first, but as an educator, I think of it as a hurdle of abstraction and syntax that I need to guide students over to get to the fun creative part and in no way basic.

With all that being said, I've opened issue #6644 for adding a range function that would allow a consistent way for handle numerical iteration and iteration over arrays without the risk of infinite loops.

@GregStanton
Copy link
Collaborator

Thanks @calebfoss! I appreciate all the work and thought you've put into this. These are the kinds of discussions that make p5.js great. To clarify the case I was making, I agree that for...of is simpler. I also agree that, in general, learning paths vary. However, I'm suggesting that beginners nearly always learn for loops already, whereas they don't always learn for...of loops; since for loops are completely general, it's better to avoid adding something new for them to learn.

It sounds like you're proposing we use for...of exclusively, so that beginners never need to learn for loops, which is an interesting idea. My initial concern is that this requires a special p5.js function, whereas looping is a foundational language feature. This means that students venturing outside of p5.js may be misled into thinking that a p5.js range() function will work in vanilla JavaScript. In the other direction, we may have users coming to p5.js after learning basic JavaScript, and they won't know about p5's special way of doing the numerical loops that you mentioned.

The users who are introduced to loops for the first time with p5's special range() function will likely be in the minority. You wrote that "I wished I had first learned to iterate using the Python structure"; I can definitely imagine this since I actually learned Python before JavaScript. But for better or worse, JavaScript is the most widely used language. And among those who are introduced to JavaScript, learning the three-statement for loops is a nearly universal experience; if the JavaScript language itself changes, then I think we'd be having a different conversation.

In general, I feel that p5.js is about opening doors and making creative coding easier in JavaScript, rather than making the underlying JavaScript language easier to work with.1 Part of the reason is that JavaScript itself opens a lot of doors. Modifying fundamental language usage also feels out of scope and carries certain risks.

Avoiding infinite loops is an interesting point. This seems like less of an issue with for loops than while loops since I don't think beginners usually stray from the basic template with all three expressions (the initialization, the condition, and the afterthought). In their minds, a condition with a < or <= operator and an afterthought that increments the variable may as well be required since they've probably never seen anything else. Have you had a different experience with this? I'm curious, but it does seem like an issue we may just have to deal with.

Footnotes

  1. It's true that p5.js makes changes like wrapping Math methods so they're in the global namespace, but that doesn't change the main syntax, and providing user-friendly wrappers for Web APIs doesn't change core JavaScript features. There are also p5.js features like append() that wrap native array methods, but notice that all but one of these are being deprecated in favor of using the native JS push() methods. I'm not sure if something similar will happen with string functions, etc.

@davepagurek
Copy link
Contributor

Avoiding infinite loops is an interesting point. This seems like less of an issue with for loops than while loops since I don't think beginners usually stray from the basic template with all three expressions (the initialization, the condition, and the afterthought).

The case where I normally end up with an infinite loop using only for loops is if I copy-and-paste a for to do a nested loop. e.g. if I'm trying to iterate in a grid:

for (let x = 0; x < 100; x++) {
  for (let y = 0; y < 100; x++) { // Notice it's still x++ here
    // ...
  }
}

Since I accidentally never modify y, the inner loop never exits. This may not be a huge issue if the p5 editor ever adds infinite loop detection though.

@calebfoss
Copy link
Contributor Author

calebfoss commented Dec 20, 2023

@GregStanton To contextualize, I teach p5 with university students, many of whom have never written code before. Infinite loops are a very common problem when they are first working with loops. In the p5 editor, it's pretty easy to leave the auto-refresh button on and end up with it running a loop that hasn't been written out completely. I've had the same thing happen in Glitch's live preview window. I make a point of warning students about this in class and in resources I share with them, but inevitably a couple students miss that and sometimes even lose unsaved progress.

I'm under the impression that many p5 users are using the tool to write code for the first time, given its emphasis on beginner-friendliness and use in introductory coding education. It would surprise me if three-statement for loops were a nearly universal experience for folks starting out with p5.

I hear you on not wanting to modify the language. I would just clarify that I'm not proposing modifying the language. For...of loops are a core feature of JavaScript. The range() function I'm proposing is simply a more beginner-friendly way to create an iterator. Similarly, you could write out the arithmetic for linear interpolation in vanilla JS, but the lerp() function makes the process more beginner friendly.

I don't necessarily think our documentation needs to stick with one single structure for iteration, but since I'm hearing that syntax that feels new is less beginner friendly (and that's a big part of my concern about array methods with arrow functions), I'm offering that if we want one consistent way to handle most cases of iteration in our documentation, this would be an option.

@GregStanton
Copy link
Collaborator

GregStanton commented Dec 21, 2023

Thanks @calebfoss. I hope I'm not being too disagreeable! The approach you proposed is pretty sweet, I think. I'm sort of playing devil's advocate in order to hash out the trade-offs.

To be clear, I'm not saying everyone who starts learning p5 has already seen for loops. I'm saying that among those introduced to JavaScript, nearly all of them will be introduced to three-statement for loops. This includes p5.js users, since the current style guide recommends for loops except in the case of array iteration, and the only kind of loop explained in the p5 reference is the three-statement for loop (here's the relevant reference page). This means that adding for...of loops will be an extra thing to learn, unless we get rid of for loops altogether.

I get that for...of is a core feature, but you're introducing a new function (range()) to be used in conjunction with for...of, in order to accomplish a task that can already be performed within the core feature set. I think this is different than lerp() and similar features that I mentioned in the footnote to my last comment, since linear interpolation isn't a core feature of programming languages like loops are. Since p5.js is meant to make creative coding easier, it totally makes sense to make such features more beginner friendly.

I guess we can wait to see whether others prefer for vs. for...of with the range() function you proposed. I'm not sure yet where I come down on this; the infinite loop issue is more compelling than I thought. I do think it's a good idea to stick with one option for reference examples if possible.

@calebfoss
Copy link
Contributor Author

calebfoss commented Dec 21, 2023

@GregStanton I think you're bringing up valid points worth considering and not being too disagreeable. Likewise, I hope I'm not coming off stubborn. I want to make a case for my suggestion, but I don't mean to invalidate other options.

I'm actually not sure how I feel about the idea of always using range(). Making that switch all at once sounds like a big change that could be disorienting for the community and a lot of work to change. I do really like the idea of including it in the library as an option, but I'll focus on documentation in this thread. I think there are significant pros and cons to each option that's been suggested, and I appreciate you all taking the time and energy to discuss.

@davepagurek
Copy link
Contributor

Yeah, it would definitely be a nice utility regardless of whether it's what we use in the documentation, and a lot of my sketches have a version of a range() function I keep copy-and-pasting in 😅

So it seems like there are two scenarios we could encounter in the docs: (1) iterations over lists, and (2) iterations that are not backed by data. The range() function helps turn the second scenario into the first. If we don't use it, that I think means that we would always be using for (let i = 0; i < max; i++) { ... } syntax for (2) right? And then we still would have to decide on for..of vs forEach for (1)? Maybe it's worth trying to decide on how we tackle (1) first, since it seems like a decision we'd have to make regardless of our use of range().

@GregStanton
Copy link
Collaborator

The range() function helps turn the second scenario into the first. If we don't use it, that I think means that we would always be using for (let i = 0; i < max; i++) { ... } syntax for (2) right?

I think so.

And then we still would have to decide on for..of vs forEach for (1)?

I proposed sticking to for for this case too, based on the reasoning in this reply. I just realized the headings I included may have made this unclear; I just updated them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants