Posted by azhenley 4 days ago
I find that keeping functions short also helps a ton with that.
No, shorter than that. Short enough that the only meaningful place to "move a block of code" is into another function. Often, by itself.
Reading and understanding code is a process of answering "what are the immediate steps of this task?", without thinking about what those steps consist of or entail. It is not a process of answering "where is the variable representing ..., and the code that manipulates this?", especially since this makes assumptions that may prove incorrect.
Yes, but also no. If its a mostly side-effect free function with a good name and well defined input/output types its basically free.
    order_data = boto.get_from_dynamodb()
    customer_data = boto.get_from_rds()
    branding_assets = boto.get_from_s3()
    return render_for_user(order_data, customer_data, branding_assets, ...)John Carmack is a C++ programmer apparently that still has a lot to learn in python.
FYI John Carmack is a true legend in the field. Despite his not being a lifelong Python guy, I can assure you he is speaking from a thorough knowledge of the arguments for and against.
Preach to the python choir bro, but it should be telling when a python bro considers it's too ergonomic and wasteful.
At some point being clean and efficient about the code is actually ergonomic, no one wants to write sloppy code that overallocates, doesn't free, and does useless work. To quote Steve Jobs, even if no one sees the inside part of a cabinet, the carpenter would know, and that's enough.
tl;dr: Craftmanship is as important as ergonomics.
We are not even talking about in-place algorithms, just 10 functions that process an html into a new array, maybe:
html = load_template(route) html = formatstring(html,variables) html= localize_paths(html) ...
And you would rather have it:
template = load_template(route) formatted_html = formatstring(template,variables) html_with_localized_paths = localize_paths(html)
And you would rather have the latter? For what gain? I think you wouldn't.
"Only a sith deals in absolutes", you have to recognize that both are valid under different contexts. And I'm merely explaining why inmutable is the default in python, 1: python doesn't do programmer self restrictions like const and private; 2: memory is automatic, so there's no explicit allocation and freeing like in C++, so using a new variable for each thing isn't a zero overhead abstraction.
Even for smaller cases, (not 50kb arrays), it's still the proper thing to do, although you have freedom to choose, it's easier to just follow one style guide and protocol about how to do things, if it's pythonic to reuse the variable name, just reuse the variable name. Don't fall for the meme of coming from another language and writing C++ in python or Java in python, you are not a cute visionary that is going to import greatness into the new language, it's much better to actually learn the language rather than be stubborn.
There's places where you can be super explicit and name things, if it's just an integer then it's a very cheap comment that's paid in runtime memory instead of in LOC. But this is why the default in python is not const, because variable name reuse is a core python tactic, and you are not our saviour if you don't get that, you are just super green into the language.
Anything else is wishful thinking, trying to rely on the GC for deterministic behaviour.
> [...] outside of true iterative calculations
Usually there are good, obvious names for intermediate calculations in my experience.
I'm open though - what kinds of things are you doing that require reassigning variables so much?
But any variable which I’ve not already marked as const is pretty much by definition going to be modified at least once. So now instead of 1 variable name you need at least two.
So now the average number of variables per non-const variable is >= 2 and will be much more if you’re doing for example DSP related code or other math heavy code.
You can avoid it with long expressions but that in principle is going against the “name every permutation” intention anyway.
It's actually math heavy code (or maybe medium heavy?) where I really like naming every intermediate. fov, tan_fov, half_tan_fov, center_x, norm_x
Which sounds really awful, but after a while it forces you to parse the logic itself instead of being guided by possibly-out-of-date comments and variable names.
I now prefer less verbosity so that probably explains why I’m a little out of distribution on this topic.
If you looked at any of my code prior to that job, it was the polar opposite with very “pretty” code and lengthy comments everywhere.
  df = pd.read_excel()
  df = df.drop_duplicates.blahblah_other_chained_functions()
  [20 cells later]
  df = df.even_more_fns()- if (x) { const y = true } else { const y = false } // y doesn't exist after the block - try { const x = foo } catch (e) { } // x doesn't exist after the try block
  const myArray = [1,2,3]
  myArray.push(4)
  myArray // [1, 2, 3, 4]  const y = (() => {
    if (x) {
      return true;
    } else {
      return false;
  })();    const y = (x === true) ? true : false;
I used this kind of style for argument initialization when I was writing JS code, right at the top of my function bodies, due to ES not being able to specify real nullable default values. (and I'm setting apart why I think undefined as a value is pointless legacy).    Composite.prototype.SetPosition(x, y, z) {
        x = (isNumber(x) && x >= 0 && x <= 1337) ? x : null;
        y = (isNumber(y) && y >= 0 && y <= 1337) ? y : null;
        z = isNumber(z) ? z : null;
        if x !== null && y !== null && z !== null {
            // use clamped values
        }
    }  function SetPosition(x, y, z) {
    if (!(isNumber(x) && isNumber(y) && isNumber(z))) {
      // Default vals
      return;
    }
    x = clamp(x, 0, 1337);
    y = clamp(y, 0, 1337);
    z = z;
  }In JS, errors are pretty painful due to try/catch, that's why I would probably these days recommend to use Effect [1] or similar libraries to have a failsafe workflow with error cases.
Errors in general are pretty painful in all languages in my opinion. The only language where I thought "oh this might be nice" was Koka, where it's designed around Effect Types and Handlers [2]
const y = x ? true : false;
    const y = (bool)x;
or    const bool y = x;val result = if (condition) { val x = foo() y = bar(x) y + k // return of last expression is return value of block } else { baz() }
Or:
val q = try { a / b } catch (e: ArithmeticException) { println("Division by zero!") 0 // Returns 0 if an exception occurs }
Edit: ugh, can't get the formatting to work /facepalm.
Makes everything so much easier to reason about.
const prevents reassignment of the variable but it does not make the object the variable points to immutable.
To do the latter, you have to use Object.freeze (prevent modification of an object’s properties, but it is shallow only so for nested objects you need to recurse) and Object.seal (prevent adding or removing properties, but not changing them).
May people use immutable.js or Immer for ergonomic immutable data structures.
This is why the single var pattern used to be recommended.
When I was first learning I thought all methods would mutate. It has a certain logic to it
  const result = ... ;
  return result;
I know debuggers can show return values, but that doesn't help when you're just doing a quick console log inspection.