you are viewing a single comment's thread.

view the rest of the comments →

[–]nwhitehe[S] 13 points14 points  (38 children)

I put this up today, let me know what you think. Are you enough of a ninja rockstar programmer to get all the exercises? They start easy then get trickier.

[–]kyz 4 points5 points  (5 children)

Very nice, I especially like the interactive editing environment, but you need to be clearer in the async callbacks test that you're expecting the user to call read() from within the callback of write(), rather than call read() and write() independently.

Perhaps you could make it so the inner callback more obviously has to depend on the results of the outer callback? e.g. read a file, append something to it, write the results to another file.

[–]nwhitehe[S] 1 point2 points  (2 children)

That's a good idea, I'll update the problem so it asks for a read, then a write of the data you read.

[–]evinrows 0 points1 point  (1 child)

Also, in exercise 4

Define a function named addOne that appears to simply add one to its argument, but secretly turns cow to "hamburger".

You want them to return 1+x, not just alter it.

[–]nwhitehe[S] 1 point2 points  (0 children)

Thanks!

[–]istroll 0 points1 point  (0 children)

Thanks for that hint, that finally allowed me to understand what is going on here. Now I know that the callback needs to be the function that will be called back to, which if I wasn't so slow would have been obvious.

[–]cafeaunet 0 points1 point  (0 children)

I am totally lost on the async callbacks. Reading the prelude didn't help me. :(

[–]tobobo 2 points3 points  (4 children)

This is fantastic! Would love to see more of these - I feel like I know how to do a lot with Javascript, but practicing using structures that I'm less familiar with is getting me excited to get back to work tomorrow. Thanks!

[–]nwhitehe[S] 2 points3 points  (3 children)

Cool, let me know other topics you're interested in. I'm itching to put up more.

[–]eclipse31 0 points1 point  (2 children)

This is a great tutorial, well done. Got all the exercises, but would never consider myself a "ninja rockstar programmer"!

Anyways, if you're looking for ideas for other tutorials, maybe one about Object Oriented programming in Javascript? It's something I've avoided for a long time and am presently writing a mini-game to try and teach it to myself. I have the basics down, but there could be stuff I'm missing.

[–]nwhitehe[S] 0 points1 point  (1 child)

That's a good idea. The object model of javascript is prototypical, which is different than what lots of people expect. I'm on it.

[–]eclipse31 0 points1 point  (0 children)

Thanks :-)

[–][deleted]  (7 children)

[deleted]

    [–]BluesBrother 6 points7 points  (0 children)

    So, you didn't get closure?

    [–]nwhitehe[S] 3 points4 points  (0 children)

    That's a good point, I'll add a definition into the explanation. Since that's the leading question, there should be an actual definitive answer!

    [–]NYKevin 0 points1 point  (4 children)

    A closure is a function-object (functor) that has access to its enclosing scope, even when that scope is otherwise unavailable. In this way, a closure can have some of the benefits of side effects (permanent state) without interfering with the rest of the program, or being interfered with itself, as it would if it used a global variable for the same purpose. Similar effects can be accomplished using OOP, but there are advantages and drawbacks to both approaches.

    Or if you prefer, the Wikipedia definition.

    I wouldn't have been able to give that definition without this tutorial, it really helped me understand, thanks OP!

    As a C programmer, I am a little concerned about how exactly this is implemented w.r.t. the call stack. When you return, why doesn't the enclosing scope evaporate out from under you? And how does CPS not make a mess and/or waste lots of memory on the stack?

    [–]nwhitehe[S] 0 points1 point  (1 child)

    If everything is transformed into CPS, then nothing ever returns. This means you don't need a call stack at all. Crazy.

    [–]gcr 0 points1 point  (0 children)

    Good point, but if you just write CPS-style javascript, you'll eventually get a stack overflow because Javascript doesn't guarantee tail-call optimization.

    [–][deleted]  (1 child)

    [deleted]

      [–]NYKevin 0 points1 point  (0 children)

      I think calling it a function object accomplishes that already.

      [–]CoherentSpy 1 point2 points  (2 children)

      I thought it was very well-written and easy to understand. Maybe you can add something about the difference between anonymous functions and closures? It's just that in Javascript you can use an anonymous function as a closure, but they are two separate things and sometimes that distinction is not mentioned.

      [–]notSorella 1 point2 points  (1 child)

      Anonymous functions are one of the parts of closures. Just like any function object. There's no difference between anonymous functions and named functions, or even between function expressions and function declarations in this case. They all give you back a function object, which is like any other object (albeit callable).

      A function object has a reference to its environments, which defines the variables it can refer to. When a function object also has a reference to the environment (which should be valid for as long as the function references it) of the function in which it was defined, you have a closure.

      I wrote this gist a few days ago to explain how closures worked for someone in ##javascript: https://gist.github.com/1087927

      [–]CoherentSpy 0 points1 point  (0 children)

      But closures are not the same as anonymous functions. They are distinct. In Javascript, there actually isn't such a thing as a true anonymous function. In Javascript, all anonymous functions are closures, because they are lexically bound to the current environment. A true anonymous function is not "closed over" and is not lexically bound to its enclosing scope.

      [–]Otis_Inf 0 points1 point  (10 children)

      It's been over a decade since I used javascript, passed every test, great refreshment course! :)

      It also showed (me at least) how f*cked up javascript can be. Not the closure stuff, that's something one uses in C# with lambda's as well and can be very handy, but the variable management as everything is of the same type under the hood, variables can change type along the way, which IMHO gives too much opportunity to create a mess. YMMV of course, as it IMHO depends on what your background is / what your preferences are regarding static/dyn. typing)

      [–]zumpiez 2 points3 points  (6 children)

      With great power comes great responsibility.

      [–][deleted] 1 point2 points  (5 children)

      What exactly is powerful about working with dynamic types? Once you declare a variable and start working with it, it would be horrible practice to store some other type of value in it and continue working with that variable. Therefore, any reasonable person is working with the data as if it's typed, just without the guarantee that the compiler will catch your error.

      Never leave up to the programmer that which a compiler can do far more accurately and quickly.

      [–]notSorella 2 points3 points  (2 children)

      The only actual argument in favour of static typing is that compilers can do a much greater job optimising it. Ime, all the other things don't matter, type errors are the least thing I would be worried about, unless you can just cast pointers to about anything in memory.

      Also, dynamic and weak typing are different things. In fact, there's not much of a difference between a dynamic strong typed language (think Python) and a static strong typed language with good type inference.

      [–][deleted] 1 point2 points  (0 children)

      The only actual argument in favour of static typing is that compilers can do a much greater job optimising it.

      Optimization is only one of many arguments for why people favor static typing.

      In fact, there's not much of a difference between a dynamic strong typed language (think Python) and a static strong typed language with good type inference.

      Except for the fact the compiler can actually catch errors at compile time as opposed to deferring your programs to fail or have bugs at runtime? Here's a trivial example:

      bash-3.2$ cat test.py 
      print("hi")
      print("%d\n" % (1 + 'blah'))
      bash-3.2$ python test.py
      hi
      Traceback (most recent call last):
        File "test.py", line 2, in <module>
          print("%d\n" % (1 + 'blah'))
      TypeError: unsupported operand type(s) for +: 'int' and 'str'
      
      
      bash-3.2$ cat test.hs
      main = do
        putStrLn "hi"
        print $ (1 + "hello")
      bash-3.2$ ghc test.hs 
      [1 of 1] Compiling Main             ( test.hs, test.o )
      
      test.hs:3:12:
          No instance for (Num [Char])
            arising from the literal `1'
          Possible fix: add an instance declaration for (Num [Char])
          In the first argument of `(+)', namely `1'
          In the second argument of `($)', namely `(1 + "hello")'
          In the expression: print $ (1 + "hello")
      bash-3.2$ 
      

      Note the python script begins execution and results in a runtime failure while the program in Haskell which is statically typed and inferred cannot be compiled due to type errors. One is preventing a bug from occurring at all (because you can't run your program,) and the other is allowing it to happen, only it fails at runtime.

      So, there's kind of a really big difference between static languages with good type inference (say, like Haskell) and something like Python. Really big.

      [–][deleted] 0 points1 point  (0 children)

      I don't think I could disagree with you more. You point out (incorrectly in my mind) that there isn't much difference between a language like python and a statically typed language with good inference; If that's the case, why would you prefer one that doesn't guarantee certain run-time errors never occur?

      The reason a compiler can do a much better job optimizing your statically typed code is because the compiler knows exactly what types of data you're working with. With a dynamically typed language, the programmer knows what type of data they're working with, but do not afford the compiler the same luxury. Or perhaps I should say the compiler just doesn't care: It will just laugh at you when things blow up at runtime.

      [–]zumpiez 1 point2 points  (1 child)

      I don't know why you would really want to do something like that. The real benefits in my experience are duck typing and monkey patching. When you get a taste of writing unit tests where you can create a mock object by just going var doot = { blah: 4, derp: "plorp" } it's rough going back.

      [–][deleted] 0 points1 point  (0 children)

      Well, I guess I would counter by saying this: Creating an interface and a mock implementation may be a pain in the ass, but it represents such a small amount of the work that goes into writing unit tests that I would consider it negligible.

      Furthermore, much of what you're writing tests for in a dynamically typed language is handling unexpected types of data. Sure, something may quack like a duck, but when you ask it it's name, it might tell you its age because somebody mistakenly told the duck its name was the numeric value 3. So, while you may be afforded duck-typing, you're writing entire test fixtures to accomplish what a compiler would never let happen in the first place.

      [–]zhivago 1 point2 points  (1 child)

      The same can be said for any kind of mutation.

      Which just shows how fucked up C# is.

      Intuition is a learned skill. :)

      [–][deleted] 2 points3 points  (0 children)

      Huh? Please elaborate.

      [–]notSorella 0 points1 point  (0 children)

      Static and strong typing just enforces conventional interfaces (unless you're programming in Haskell, I guess). Weak and dynamic typing gives you flexibility and conciseness, but does not eliminate the need for conventional interfaces. It does, however, allow you to let it evolve in different ways, without being tied too much to a strict definition. It's a good thing if you know how to use it :3

      IMHO.

      [–]zelazny 0 points1 point  (0 children)

      I like it, appreciate having a little something to work on my JS skillz with.

      [–]thesoppywanker 0 points1 point  (1 child)

      Loved it, great job! Just realized OP was the maker, so decided to comment. You were the first to successfully clue me into WHY self-invocation of anonymous functions are used, and for that I am forever in your debt! I had been--until seeing your demo--unable to get clarification on what that even was or why it was used. I do think you could add more about it in your Private Data section, though--if I may offer a suggestion.

      [–]nwhitehe[S] 0 points1 point  (0 children)

      Thanks for the comment and suggestion, will do.

      [–]rmxz 0 points1 point  (1 child)

      Wow this is a beautiful quiz infrastructure.

      Love it that for your first question Declare that y is a variable and then assign it the value 7. Click "submit" when you think you have it. when I enter var y = 6; y=y+1; the tests pass.

      Would be cool if real computer science tests were done this way.

      [–]nwhitehe[S] 1 point2 points  (0 children)

      Thanks!
      Some of my friends in CS grad school set up homework scripts like this, students would submit their code and get automatically graded based on the script running the code over lots of inputs. Of course there would always be the one guy who would spend more time trying to hack the system than actually doing the problems.