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

[closures] Concept examples and explanation needs improvements #1529

Open
Tracked by #1415
Jasstkn opened this issue Nov 18, 2021 · 21 comments
Open
Tracked by #1415

[closures] Concept examples and explanation needs improvements #1529

Jasstkn opened this issue Nov 18, 2021 · 21 comments
Labels
x:action/improve Improve existing functionality/content x:knowledge/none No existing Exercism knowledge required x:module/concept Work on Concepts x:size/medium Medium amount of work x:type/content Work on content (e.g. exercises, concepts)

Comments

@Jasstkn
Copy link
Contributor

Jasstkn commented Nov 18, 2021

Hi.
While I was learning concepts for JavaScript I found out that closures theory isn't very helpful for solving the suggested practical exercise: Coordinate Transformation.

I would like to propose to improve it by

  • adding a more advanced example with an explanation for using a function in return
  • explain how to work with its arguments then.

Let me know what do you think.
Connected links are here:
https://exercism.org/tracks/javascript/concepts/closures
https://exercism.org/tracks/javascript/exercises/coordinate-transformation

@junedev
Copy link
Member

junedev commented Nov 18, 2021

Creating an issue about improving the closure concept was on my todo list. 🙂

In addition to adding to the existing concept documents I think it would also be good to maybe rework/simplify some of the existing content.

Do you want to work on this?

@Jasstkn
Copy link
Contributor Author

Jasstkn commented Nov 19, 2021

Creating an issue about improving the closure concept was on my todo list. 🙂

In addition to adding to the existing concept documents I think it would also be good to maybe rework/simplify some of the existing content.

Do you want to work on this?

Yes. I can try :)

@junedev junedev added the x:status/claimed Someone is working on this issue label Nov 19, 2021
@khendrikse
Copy link
Contributor

Hi @junedev! I don't know if this is the place for this, but I notice something about the closure explanation.

In the explanation it says:

"Closures are a programming pattern in JavaScript which allows variables from an outer lexical scope to be used inside of a nested block of code. JavaScript supports closures transparently, and they are often used without knowing what they are."

// Top-level declarations are global-scope
const dozen = 12;

{
  // Braces create a new block-scope
  // Referencing the outer variable is a closure.
  const twoDozen = dozen * 2;
}

// Functions create a new function-scope and block-scope.
// Referencing the outer variable here is a closure.
function nDozen(n) {
  return dozen * n;
}

But most explanations, including the one on MDN define a closure as being A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function. (source: MDN on Closures

In the explanation it says that referencing the outer variable from the first block scope would already be a closure. Looking at the docs from MDN, that would then not seem to be correct, as that first block scope is not a function?

I'm mostly asking because it is confusing me as well, and as closures are a hard concept to explain, I wonder if we might need to make sure this explanation is correct.

@junedev
Copy link
Member

junedev commented Nov 29, 2021

Great question! I am not 100% sure myself. @SleeplessByte, what's your opinion on this? Would you can the block example a closure as well?

In any case, I would recommend making all the main content about the function version of closures as this is what most people think of when they hear the term and how they are mostly used. Then if we decide the blocks count as closures as well we can add that as an additional knowledge nugget in about.md.

@khendrikse
Copy link
Contributor

I'm curious what @SleeplessByte thinks (if only to learn ;)), if y'all think it's better to delete the first shown block-scope, let me know, I'll remove it and open a PR.

@junedev
Copy link
Member

junedev commented Nov 29, 2021

@khendrikse Either @Jasstkn will already incorporate this in their current re-work of the concept files or it can be addressed afterwards. In any case, I would like to avoid you and @Jasstkn working on the same files at the same time.

@khendrikse
Copy link
Contributor

That is also fine :)

@SleeplessByte
Copy link
Member

SleeplessByte commented Nov 29, 2021

Sooooo. 👋🏽

Definitions are as difficult as naming is (I wrote articles about similar subjects in the past, for example: JavaScript, Ruby, and C are not call by reference), which is what makes this even more interesting. Yes, in general closures are often defined as enclosing values inside a function, which is often possible in languages that "do not treat functions as a special case" (meaning functions are first-class citizen, and can be passed around, just like other values). This is usually mentioned because if you can pass functions around, it means you can often execute the function on a completely different call site then where it was defined. The operational bonus is then that that function has access to variables it had access to when it was defined despite the call site NOT (necessarily) being able to access those.

Okay. Great.

In JavaScript blocks (the first example) cannot be passed around, but in some other language (hello there Ruby), some forms of blocks are callable (effectively making them similar or the same as functions), ánd can be passed around.

Because the block from the example is static/fixed in place, its power to enclose the variable is "lost". It doesn't matter. Therefore I would agree to bin the example, despite you probably being able to find references where a writer argues that any enclosure is "a closure". I'm not willing to say that we absolutely cannot call this a closure, but in general, when people who know JavaScript or similar languages talk about closures, they will almost always refer to instances of functions that enclosed variables, and thus not talk about blocks.

TL;DR; correctness of the first example doesn't matter. It's use-case is basically none (other than creating a new block which can be great when writing switch statements and their cases. I would vote to remove it.


✨ Everything that follows is just extra information and should not be included in this exercise

Are there other ways to enclose other than using a function?

Yes! For example classes can do this too.

const definedOutside = 'hello, world!'

export class Shout { 
  // This function is technically a closure
  call() { console.log(definedOutside); }
  
  // This variable is, once this proposal becomes language,
  // technically enclosing a value from outside this class, but will
  // probably not be called a closure
  #privateVar = definedOutside
}

const utterance = new Shout()

utterance
// => Shout {#privateVar: 'hello, world!'}

utterance.#privateVar
// => SyntaxError Private field '#privateVar' must be declared in an enclosing class

utterance['#privateVar']
// => undefined

utterance.call()
// => 'hello, world!'

Classes can in fact enclose values passed from the outside in their constructor, variables created in their constructor, or even variables attached to themselves

export class Enclosing {
  constructor(passedValue) { 
    let increment = 0
    
    this.magicNumber = 21
    const self = this;
     
    // Return a new object that does NOT have Enclosing as its prototype

    return {
      // encloses variable defined in constructor
      increase() { 
        increment += 1
        return increment;
      },
      
      // encloses variable passed in constructor
      call() { console.log(passedValue); },

      // encloses the entire object that was created by the constructor
      ref() { return self.magicNumber * 2 }
    }
  }
}

// Now try it out
const enclosed = new Enclosing('Goodbye, Mars!')

enclosed.magicNumber
// => undefined

enclosed.increase()
// => 1

enclosed.call()
// => "Goodbye, Mars!"

enclosed.ref()
// => 42

@khendrikse
Copy link
Contributor

Thanks so much @SleeplessByte ☺️ I learned a lot and this made it very interesting! I am glad I decided to ask. (Also, this read as well as a blog already haha).

@SleeplessByte
Copy link
Member

No problem! Gewoon blijven vragen :)

@junedev
Copy link
Member

junedev commented Jan 19, 2022

@Jasstkn How is it going? Do you still want to work on this?

@junedev
Copy link
Member

junedev commented Feb 3, 2022

@Jasstkn Since I haven't heard from you in a long time, will un-assign you from this issue for now so someone else could pick this up.

@junedev junedev added x:action/improve Improve existing functionality/content x:knowledge/none No existing Exercism knowledge required x:module/concept Work on Concepts x:size/medium Medium amount of work x:type/content Work on content (e.g. exercises, concepts) and removed x:status/claimed Someone is working on this issue labels Feb 3, 2022
@SnowJambi
Copy link
Contributor

SnowJambi commented Feb 5, 2022

+1 on this. I'm doing this exercise for the first time, and I had no idea what I was being asked to do, and I couldn't manage to glean much context from the test cases either.

I haven't finished it yet, but particularly task 1 seems to be lacking information for someone who has never encountered this before.

Intuitively, if I were to write the translate2d function myself, I would write something like

function translate2d([x, y], [dx, dy]) {
  return [x + dx, y + dy]
}

let coords = [1, 2]
console.log(translate2d(coords, [2, 3])
// [3, 5]

Ok, cool, but now looking at the task 1 information

const moveCoordinatesRight2Px = translate2d(2, 0);
const result = moveCoordinatesRight2Px(4, 8);
// result => [6, 8]

My first thought is how on earth does the arguments from moveCoordinatesRight2Px get passed in to translate2d? This is something that hasn't been seen so far in the learning track, and it's dropped on us with no explanation apart from stating that translate2d should return a function.

It wasn't until I somehow just happened to stumble upon the answer, that I was able to work backwards from there and understand what is happening.

function translate2d(dx,dy) {
  return function translate(x, y) {
    return [x + dx, y + dy]
  }
}

console.log(translate2d(2,3))
// function translate(x, y) { return [x + dx, y + dy]; }
// ^ Aha!
// From that...
console.log(translate2d(2,3)(1,1))
// [3,4]

I'm not sure if I accidentally missed a lesson before this one or if I'm just not the brightest around, but some more information would be extremely useful, maybe as @Jasstkn mentioned an example of returning a function to give the user some more guidance towards the solution, because as it is, I'm pretty sure if I didn't have some prior experience and this was my first time learning I would be hard stuck here for sure.

@SleeplessByte
Copy link
Member

My first thought is how on earth does the arguments from moveCoordinatesRight2Px get passed in to translate2d? This is something that hasn't been seen so far in the learning track, and it's dropped on us with no explanation apart from stating that translate2d should return a function.

You haven't seen it thus far, because we make sure that you don't "see" something until you've had the concept about it, which this exercise is supposed to provide.

However, the current introduction.md and instructions.md not providing enough information is exactly why we want it to be improved! So all input is welcome :)

@junedev junedev changed the title [closures] Concept examples and explanation improvements [closures] Concept examples and explanation needs improvements Jun 27, 2022
@sahaankit
Copy link

hey @junedev I would love to work on this issue!

@junedev
Copy link
Member

junedev commented Jan 6, 2023

@sahaankit I am sorry but we currently don't except community contributions. Here the official notice with a link with more info.

We are currently in a phase of our journey where we have paused community contributions to allow us to take a breather and redesign our community model. You can learn more in this blog post.

@safwansamsudeen
Copy link
Contributor

Hi @junedev, I think you still don't accept community contributions, but is it OK if I open a topic on the forum to discuss this and then submit a PR based on the opinions? I feel this is an important part of improving the JS syllabus, along with promises!

@junedev
Copy link
Member

junedev commented Apr 15, 2023

@safwansamsudeen A forum thread to get to a good plan for this issue is definitely appreciated and a good idea. As for the PR, I can make no guarantees at the moment. The JavaScript track does not have any active maintainers currently.

If you are very confident, that you understand enough about how concept exercises on Exercism are supposed to look like, have very good JavaScript and English skills, you can make a PR and I can quickly check and approve. (Probably good to ping me with whatever the final ideas where from the forum thread beforehand.)
However, if the PR needs a lot of review rounds to get in shape, I will not have time for that and it is unlikely to get merged in the current maintainer situation.

@Zumh
Copy link

Zumh commented Jun 28, 2023

Is this still available?

@safwansamsudeen
Copy link
Contributor

safwansamsudeen commented Jun 28, 2023

@Zumh here's the forum thread for it. I'd appreciate it if you could voice your ideas there.

I didn't actually work on it, as I didn't really know much about Exercism back then, but I'm willing to give it a go now.

If you think you can do it, though, please work on it (as long as junedev approves, see previous comment) - and if you find it too hard, I'll take over 🙂.

@DPirate
Copy link

DPirate commented Jan 17, 2024

Therefore I would agree to bin the example, despite you probably being able to find references where a writer argues that any enclosure is "a closure".

Won't that dilute learner's understanding of how the language is designed? Understanding closures helped me digest the idea of classes. Also have some fun defining a function conditionally, instead of running whole logic of selecting every time the function runs I could just run the favourable flavour of the function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
x:action/improve Improve existing functionality/content x:knowledge/none No existing Exercism knowledge required x:module/concept Work on Concepts x:size/medium Medium amount of work x:type/content Work on content (e.g. exercises, concepts)
Projects
None yet
Development

No branches or pull requests

9 participants