Skip to content

Incorrect Analysis: JavaScript analyzer makes little sense when a object literal is used #127

Open

Description

@joshgoebel

Describe the incorrectness

I don't think the analyzers advice makes any sense here:

Using a helper method is good practice, because it replaces a cryptic "member call" with a named call that can be documented individually.

The advice assumes a student is using an array - in which case there is complexity to hide and I agree a function as a named abstraction is useful, but in this case a well-named lookup table is serving the same purpose as a function, so the advice makes little sense.

Which exercise

Resistor Duo

Source file(s)

export const decodedValue = (colors) => {
  const resistorBands = {
    black: 0,
    brown: 1,
    // ...
    grey: 8,
    white: 9
  }
  return resistorBands[colors[0]]*10 + resistorBands[colors[1]];
  
};

Expected analysis

None. (well at least not on this point)

  • You could recommend destructuring of the arguments of course - which I would as a mentor.
  • Hoisting the constant to a global.

Additional context

None.

Activity

  1. SleeplessByte commented on Oct 8, 2021

    @SleeplessByte
    Member

    I do think that:

    function colorValue(color) {
      return resistorBands[color]
    }

    is more idiomatic and readable than

    resistorBands[firstColor]

    ...but I do agree it's less problematic.

  2. joshgoebel commented on Oct 8, 2021

    @joshgoebel
    Author

    But really that's a just naming concern, IMHO. (I personally think colorValue is not great naming here) The lookup table should be named such as to fully indicate its purpose.

    So I don't see a significant difference in expressiveness between:

    // object literal
    COLOR_to_RESISTANCE[tensColor] * 10 + COLOR_to_RESISTANCE[onesColor]
    
    // function dispatch
    colorToResistance(tensColor) * 10 + colorToResistance(onesColor)

    Wrapping the object literal in a function dispatch just starts to feel like needless abstraction to me - and I've certainly steered students aways from such needless wrapping in other cases for sure.

    I don't feel the auto-intelligence should flag either of the above solutions.

  3. SleeplessByte commented on Oct 8, 2021

    @SleeplessByte
    Member

    That's a good case. Ok. I agree with your assessment :)

  4. added
    x:action/improveImprove existing functionality/content
    x:type/codingWrite code that is not student-facing content (e.g. test-runners, generators, but not exercises)
    help wantedExtra attention is needed
    on Oct 8, 2021
  5. Steffe-Dev commented on Feb 8, 2025

    @Steffe-Dev
    Contributor

    Hi there @SleeplessByte! I'm keen to make my first PR to Exercism and this seemed like a good option.

    1. I agree that adding a helper function here is good practice when the object used to find the number that corresponds to the color is cryptically named (like colors).
    2. But, I also agree that a well-named object is just as clear as a helper.

    So, I propose that we not change the analysis itself, since it should still continue to catch case (1) above, but rather expand the message to explicitly include commentary on case (2). My working draft changes this:

    💬 Using a helper method is good practice, because it replaces a cryptic "member call" with a named call, that can be documented individually.

    to this:

    💬 Using a helper method is good practice, because it replaces a cryptic "member call" with a named call, that can be documented individually. That being said, if the student uses an `Object` with a descriptive name, like `colorToResistance`, leading to code like `colorToResistance[firstColor]`, that's also fine, since the question of clarity posed above would be addressed by the descriptive name.

    Let me know if you think this would add value, or if we should opt for a different solution.

  6. joshgoebel commented on Feb 8, 2025

    @joshgoebel
    Author

    I like it.

  7. SleeplessByte commented on Feb 9, 2025

    @SleeplessByte
    Member

    Whilst the comment change is a good one @Steffe-Dev, we are no longer generating comments for mentors, and everything is done directly on solutions.

    So instead, detecting the code @joshgoebel opened the post with with a message that the const should be hoisted outside of the function is probably the way forward.

  8. Steffe-Dev commented on Feb 9, 2025

    @Steffe-Dev
    Contributor

    I see. I can definitely try to implement that. Is there an existing template for the hoisting of variables, or would we have to add one?

    Otherwise, I'm still uncertain about what we want to do about the warning relating to the helper method. Because, even if you have the constant as a global, the analyzer would still suggest that you use a helper method instead of indexing the object directly (at least when I run it locally), which I think is what @joshgoebel really wanted to address with this issue. Do we want to keep that warning for now, or should I try to suppress it?

  9. SleeplessByte commented on Feb 10, 2025

    @SleeplessByte
    Member

    It should be suppressed. So your options are:

    • Does it use an object?
      • If yes, but defined inside, please hoist
      • If yes, and defined outside, all good
    • otherwise...
      • usual messages including helper methods

    I think there is already logic to see if we are in "array" or "object" mode, so the helper check probably just needs to be _moved.

    I think there is already a template to define a top-level constant. That's probably the term you want to look for.


    Please note that the template content inside this repo is purely for development. The real messages live in website-copy

  10. 3 remaining items

  11. Steffe-Dev commented on Feb 12, 2025

    @Steffe-Dev
    Contributor

    @SleeplessByte So, after hacking away at this for a while, I realised that we have two distinct problems to solve:

    1. Detect if a constant was defined inside a method, and not at the top level of the file. This can be an array or an object.
    2. If we have a top level object defined, we should not bother with comments about helper methods.

    When you inspect the code inside ResistorColorDuoSolution.ts, especially isOptimalMathSolution, you will see that the helper method's existance is deeply entwined with many of the other math solution checks, which means a more significant refactor would be needed to properly separate the helper method logic from the other logic to such an extent that the helper method check can be bypassed independently. This means that (2) potentially has a good deal of scope creep.

    My proposal is that we start by solving (1) in its own PR, which is a self-contained check that can be inserted into the various optimal check methods, which does an early return before the helper method checks, and would thus address @joshgoebel specific request (with the source file at the top of this issue) that the error message be replaced by something more useful (the PREFER_EXTRACTED_TOP_LEVEL_CONSTANT template).

    Then we can re-evaluate the path forward of this issue. Let me know what you think, if there is a better option then I am open. 😄

  12. SleeplessByte commented on Feb 12, 2025

    @SleeplessByte
    Member

    Yah sounds fine with me!

    You could also check if the TS analyzer has a better base here with less intertwining

  13. Steffe-Dev commented on Feb 13, 2025

    @Steffe-Dev
    Contributor

    @SleeplessByte Unfortunately the TS analyzer only has Two Fer at the moment 😅

    I created a PR for what I described above, but the bot automatically closed it. I'm still learning your processes, so I don't know how to avoid that, and I would appreciate help with getting it open again. Once open, there is also a pending commit that will come through that removes an unused variable that I initially forgot about. Thanks in advance 😄

  14. SleeplessByte commented on Feb 13, 2025

    @SleeplessByte
    Member

    All good. We manually reopen it if there's merit. Which there is!!!

  15. added 2 commits that reference this issue on Mar 5, 2025
  16. added 2 commits that reference this issue on Mar 13, 2025
  17. SleeplessByte commented on Mar 26, 2025

    @SleeplessByte
    Member

    @Steffe-Dev is there now another change necessary?

  18. Steffe-Dev commented on Mar 26, 2025

    @Steffe-Dev
    Contributor

    Hi @SleeplessByte
    Well, the current state (after the PR above) is such that if a constant is declared inside the fuction then the feedback to the student will be to hoist it out instead of that they should use a helper function. However, if the constant is defined on the outside, it will still prompt them to use a helper function if they have not done so.
    After working with the code I reached the conclusion that separating the helper function check from all the other checks that depend on it (many of the math checks) would require a significant refactor, so you will have to decide if that is worth it for the gain of being able to turn the helper method feedback off if they used an object instead of an array. I unfortunately don't currently have the appetite for that.

  19. SleeplessByte commented on Mar 26, 2025

    @SleeplessByte
    Member

    No problemo. Thank you for working on the first part!

  20. joshgoebel commented on Apr 23, 2025

    @joshgoebel
    Author

    Glad to see some progress on this. :-). Thanks for the help @Steffe-Dev!

  21. Steffe-Dev commented on Apr 25, 2025

    @Steffe-Dev
    Contributor

    My pleasure. 🚀

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

Metadata

Metadata

Assignees

Labels

good first issueGood for newcomershelp wantedExtra attention is neededx:action/improveImprove existing functionality/contentx:knowledge/elementaryLittle Exercism knowledge requiredx:module/analyzerWork on Analyzersx:size/smallSmall amount of workx:type/codingWrite code that is not student-facing content (e.g. test-runners, generators, but not exercises)

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions