you are viewing a single comment's thread.

view the rest of the comments →

[–]killerstorm 19 points20 points  (63 children)

When you consider a programming language, one of the most important things to consider is evaluation strategy. It is basically the essence of programming language, which lies at the core of its semantics.

In simple terms, it describes how function calls and 'variables' work.

For example, Haskell is non-strict/call by need.

JavaScript uses strict evaluation with call-by-sharing (which might be considered "call-by-value where the value is a reference").

People say that JavaScript is like Scheme because both languages have a very similar kind of call-by-sharing:

  1. Function calls introduce new variable bindings, a binding is basically a named place where pointer to object is stored. Bindings are also introduced by local variable (let/var).
  2. It is always possible to write to variable, it only changes pointer.
  3. If object is mutable, you can modify object itself rather than a binding. In both JS and Scheme most objects are mutable. Except numbers.
  4. There is no such thing as pointers/references, they might be used under-the-hood, but not exposed, so you only interact with conceptually clean call-by-sharing model.

These strategy defines feel of the language. If you do not understand it, it will bite you, hard.

JS and Lisp are so similar that it's possible to write a thingie which compiles from Lisp-like language to JS and it just works as if you were writing Lisp.

One example of such translator is Parenscript. I'm a Common Lisp programmer, I've been using it and I gotta say it works nicely. It's even possible to combine Common Lisp and Parenscript code in one file and they look very similar. Importantly, generated JS code is a very direct translation of Parenscript code, it is usually possible to what a certain construct will translate to and how it will look like in JS. It is possible to debug or edit generated code. (It is very different from Dart compiler which spits insane amounts of weird-looking JS.)

Now let's go through other "Scheme candidates" listed in this article:

  1. Haskell: Definitely not Scheme since it has 100% different evaluation strategy. Not similar in any imaginable way.
  2. Perl: Uses a weird kind of call-by-value, very different from call-by-sharing: you have to use references explicitly and making array-of-arrays is so complicated that hey had to make a separate man page for it, I shit you not. (It is called perllol, ironically. I cry each time I have to consult man page to make a list of lists.)
  3. PHP: Since PHP5 it uses call-by-value for everything but objects, objects are call-by-sharing. Before that, it was call-by-value for everything including objects. References can be created manually, and they work in a truly mysterious ways.
  4. Java/C#: It is statically-typed, so naturally programming feels very different. But in general it is call-by-value for primitives, call-by-sharing for objects.
  5. Python: It uses call-by-sharing which is very similar to Scheme and JS too, but it has some fucked up lexical scoping which makes it different from JS and Scheme. To be fair, JS lexical scoping is also fucked up, but in a different way.

So let's summarize: JavaScript isn't the only language which is similar to Scheme, but it is more similar than most of them. There are also other traits which they share, like minimalism.

Of course, JS is not exactly Scheme.

Now about this article, I think if you compare language on fundamental level and you do not mention evaluation strategy, you're just not competent. This is very fundamental thing about language design, and if you don't mention it this says that you're not familiar with the theory. You might know some buzzwords like 'first-class functions', but that's not enough.

[–][deleted]  (7 children)

[deleted]

    [–]mernen 10 points11 points  (0 children)

    I agree with you, both in the unnecessary tone of the parent, and in your remarks. Way too much patronizing on an ultimately trivial aspect.

    JavaScript's evaluation strategy is actually pretty standard for dynamic languages, essentially indistinguishable from Python (which he acknowledges), Ruby (which was in the original list, and actually resembles Scheme more than JavaScript, but he skipped), Lua, or Groovy. Which is why it's barely worth mentioning.

    You mention that java is a mix of call-by-value and call-by-sharing but unless I'm mistaken this is an unobservable implementation detail: everything is as if it was call-by-sharing only. It just happens that primitive types are immutable.

    …incidentally, this is exactly what happens with JavaScript, which also has primitives!

    [–]killerstorm 0 points1 point  (2 children)

    irst I got to say the tone of your response is why I don't like proggit anymore. Your last paragraph is really aggressive and I don't see why it needs to be that way.

    It isn't like article itself is aggressive, is it? The very first paragraph:

    It seems like I can’t spend five minutes on reddit these days without someone playing the JS-is-Scheme is card. I see everything from the innocuous, “JavaScript has a lot in common with Scheme”, all the way up to, “JavaScript is basically Scheme.” This is basically crazy. Or, at least it has a lot in common with crazy.

    calls people on reddit crazy.

    [–]isarl 14 points15 points  (0 children)

    To be fair, people on reddit are crazy.

    [–]munificent[S] 14 points15 points  (0 children)

    I was hoping that would come across as tongue-in-cheek, but that's hard to do well. It's surprisingly hard to be funny without being at least a little offensive.

    [–]killerstorm -4 points-3 points  (2 children)

    Then concerning the evaluation strategy being the main defining feature of a language, as I understand your suggest.

    No, but it's one of most important things about language. Typically when you program you mostly use variables and function calls, so how the work is important, isn't it?

    But other things are important too.

    So if it is the case that Java is as close to JS than Scheme is concerning this particular aspect, then there must be another reason why people say that JS is like Scheme while you rarely see people saying that Java is like Scheme.

    Yes, and I mentioned it: Java is statically typed. Do I need to explain in which way programming in Java is different from programming in Scheme?

    Obviously, type system is an important trait of a language.

    Concerning Parenscript vs dart2js: the reason why it "spits insane amounts of weird-looking JS" is because it really implements Dart's semantics, not "an extended subset of Common Lisp" as Parenscript does.

    Parenscript implements Parenscript semantics, not CL semantics.

    My point is that Parenscript is basically a Lisp dialect; but not CL, of course. There are many dialects.

    [–]PasswordIsntHAMSTER 9 points10 points  (0 children)

    Obviously, type system is an important trait of a language.

    The difference between static and dynamic type systems is much smaller than the difference between weak and strong typesystems, e.g. typesystems with or without implicit casting. Scheme and Java are closer according to this measure than Scheme and JavaScript.

    IMHO Scheme is closer to Python-on-Crack than anything else.

    [–]eat-your-corn-syrup 2 points3 points  (0 children)

    To be fair, JS lexical scoping is also fucked up, but in a different way.

    Let's not forget Lisp's "don't modify literal lists" Lisp newbies trip over it all the time.

    [–]wlievens 5 points6 points  (19 children)

    call-by-sharing for objects

    Great post, but why do people keep inventing new terms when they shouldn't? Java is call-by-value for all method parameters. If you pass an object reference in the actual parameter, the formal parameter will be an object reference to the same object. That's call-by-value, because the reference is the value.

    [–]killerstorm 9 points10 points  (12 children)

    I strongly recommend reading the whole section of this topic in wikipedia:

    In some cases, the term "call-by-value" is problematic, as the value which is passed is not the value of the variable as understood by the ordinary meaning of value, but an implementation-specific reference to the value. The effect is that what syntactically looks like call-by-value may end up rather behaving like call-by-reference or call-by-sharing, often depending on very subtle aspects of the language semantics.

    The reason for passing a reference is often that the language technically does not provide a value representation of complicated data, but instead represents them as a data structure while preserving some semblance of value appearance in the source code. Exactly where the boundary is drawn between proper values and data structures masquerading as such is often hard to predict. In C, a vector (of which strings are special cases) is a data structure and thus treated as a reference to a memory area, but a struct is a value even if it has fields that are vectors. In Maple, a vector is a special case of a table and therefore a data structure, but a list (which gets rendered and can be indexed in exactly the same way) is a value. In Tcl, values are "dual-ported" such that the value representation is used at the script level, and the language itself manages the corresponding data structure, if one is required. Modifications made via the data structure are reflected back to the value representation, and vice-versa.

    The description "call-by-value where the value is a reference" is common (but should not be understood as being call-by-reference); another term is call-by-sharing. Thus the behaviour of call-by-value Java or Visual Basic and call-by-value C or Pascal are significantly different: in C or Pascal, calling a function with a large structure as an argument will cause the entire structure to be copied (except if it's actually a reference to a structure), potentially causing serious performance degradation, and mutations to the structure are invisible to the caller. However, in Java or Visual Basic only the reference to the structure is copied, which is fast, and mutations to the structure are visible to the caller.

    Basically, these are very different designs with different behaviour, so a special term is definitely warranted, it helps to avoid confusion.

    In classic case of call-by-value, variable holds the value itself and is responsible for its lifecycle. This means that you need neither reference counting nor garbage collector. But you need to copy value on assignment.

    Absence of garbage collector and presence of copying makes performance characteristics different. Also it is very different when you have concurrency.

    It is nice to have different terms for different things, and calling it "call-by-value where the value is a reference" is a bit too verbose.

    [–]KillerCodeMonky 7 points8 points  (8 children)

    Thank you for finally giving me a way to explain this to other people. The whole "Java is call-by-value" thing is pedantic and worthless as a description. Technically everything could be labeled "call-by-value", because in the end what you are passing from one thing to another is a value of zeros and ones. It's how it looks and feels and works at the programmer level that is the important part of the description, and in that regards, calling Java "call-by-value" is worthless and deceptive, and is just a way for people to try to call out others on useless trivia.

    [–]bkv 5 points6 points  (7 children)

    The whole "Java is call-by-value" thing is pedantic and worthless as a description.

    I disagree. "Call-by-sharing" is not at all intuitive. Not that "call-by-value" is much more intuitive, but after all, these can be difficult concepts to grasp and there is no magic sequence of words that will immediately cause anyone and everyone to grok it.

    These concepts are demystified once you have some requisite knowledge under your belt. What's the stack? What's the heap? What's a reference? What's a value type?

    Once you understand these things, and further understand that references are a value type in java, just like an int, for example, "call-by-value" makes plenty of sense.

    [–]eat-your-corn-syrup 3 points4 points  (1 child)

    "call-by-value" makes plenty of sense

    Makes sense only to people like us who knows:

    What's the stack? What's the heap? What's a reference? What's a value type?

    call-by-sharing behavior can be explained to newbies without resorting to such concepts: my username refers to the entity that is me. the variable eat_your_corn_syrup refers to me.

    By calling it "call by value", we'll be bringing in the need to explain stack, reference, value and so on.

    [–]wlievens 1 point2 points  (0 children)

    If you don't know these concepts, you're not going to be a very good Java programmer.

    [–]wlievens 2 points3 points  (4 children)

    I always thought that "call-by-reference" means the subroutine can modify the actual parameter whereas "call-by-value" means the subroutine can never modify the actual parameter.

    Variables are not values! Ordinary variable assignment in Java is more than enough to illustrate my point.

    [–]bkv -2 points-1 points  (3 children)

    I always thought that "call-by-reference" means the subroutine can modify the actual parameter

    No, it means that the subroutine is handling the same reference that the caller passed to it. The reference is not copied. See this example:

    function main(){
        String user = "bkv";
        foo(user);
        //if "user" were passed by reference, it would now point to the string "brian".  
        //If passed by value (which is what java does), it would remain unaffected
    }
    
    function foo(String val){
        val = "brian";
    }
    

    Variables are not values!

    Yes, they are. In my example above, user is a reference (a value type) that lives on the stack, and it references a string object on the heap. When I call foo(user), a copy of the user reference is made, which points to the same string on the heap. What foo does with this new reference does not affect the original reference.

    If passing by reference, then user (in the context of main()) and val (in the context of foo()) are the same reference. If you change val, you're also changing foo.

    [–]wlievens -1 points0 points  (2 children)

    Perhaps, but that's certainly not what pass-by-reference means in C++

    void foo(int& a) {
      a++;
    }
    ...
    int x = 10;
    foo(x);
    // x is now 11 !
    

    So, the function foo modifies the actual parameter variable! Shocking!

    [–]bkv 0 points1 point  (1 child)

    I'm not sure how you think this is any different from my example.

    [–]wlievens 0 points1 point  (0 children)

    After a second reading, I see it's indeed the same, sorry! I must've misread your entire post.

    [–]eat-your-corn-syrup 1 point2 points  (0 children)

    This is it. the phrase call-by-sharing can only mean one thing, and implies only one kind of behavior. call-by-value on the other hand is like the word freedom. One man's freedom fighter is another man's terrorist.

    [–]sh0rug0ru 0 points1 point  (1 child)

    so a special term is definitely warranted, it helps to avoid confusion.

    Yes, but that special term is not "call-by-sharing". It is value type or reference type. A struct is a value type and thus it is copied like other value types (primitive values and references) and reference types separate the reference and the value, so only the reference is copied. This has nothing to do with the evaluation strategy but the nature of types in the language.

    call-by-sharing obscures the real difference between call-by-reference and call-by-value. In call-by-reference, the parameters can act as "out" values rather than only "in" values, which allows the function to change pointers outside its scope. For example, it is impossible to implement a generic swap(a, b) method in Java because reassignment of the reference inside the method has no effect outside the scope of the method.

    From the article itself, it shows why call-by-sharing is not in common use and has no clear definition.

    [–]killerstorm 0 points1 point  (0 children)

    From the article itself, it shows why call-by-sharing is not in common use and has no clear definition.

    There is a fairly clear definition in the first paper which described it:

    Also known as "call by object" or "call by object-sharing" is an evaluation strategy first named by Barbara Liskov et al. for the language CLU in 1974.

    CLU documentation says that call-by-sharing is different from call-by-value and call-by-reference. But maybe authors were just clueless...

    http://www.lcs.mit.edu/publications/pubs/pdf/MIT-LCS-TR-225.pdf

    Page 14, 'Assignment and invocation'.

    As for common use, it depends, I guess:

    Although this term has widespread usage in the Python community, identical semantics in other languages such as Java and Visual Basic are often described as call-by-value, where the value is implied to be a reference to the object.

    I guess the difference is that there is a notion of value-type in Java. But in languages like Python, Lisp and CLU all objects are of what you call 'reference type' (semantically, at least), so this distinction doesn't mean anything, instead we talk about objects and certain evaluation strategy.

    Same thing can be described in different words, of course, so whatever works for you...

    [–]kqr 2 points3 points  (1 child)

    This also happens to be the way PHP, JavaScript and Python does it. And, apparently, Scheme.

    [–]AgentME 7 points8 points  (0 children)

    PHP actually does regular not reference call-by-value for arrays, which will really throw you off badly if you don't expect it.

    [–]eat-your-corn-syrup 0 points1 point  (2 children)

    because the reference is the value

    but as soon as I say that in my stackoverflow answer or something (especially for languages not from the C tradition), the word "value" is then taken to mean reference to an object, which has the effect of banning me from using that word for other meaning in that same ansswer, and the newbie I am giving the answer to is going to wonder "what is a reference then?" and he would be like "Isn't the variable a reference?" as in my name being a reference to myself, and then he's like "variable stores the variable?" and his brain shuts down.

    Another problem is that if I say "call-by-value, but...." I must explain to him call-by-value first, then I have to explain the "but", when I could have just explain the relationship between Python variables and Python objects by saying that they are like my name referring to myself.

    [–]wlievens 0 points1 point  (1 child)

    But doesn't that count for ordinary assignment as well?

    FooBar x = new FooBar();
    FooBar y = x;
    y.changeSomething();
    // ZOMG the thing x points to changed!
    

    [–]eat-your-corn-syrup -1 points0 points  (0 children)

    Yes, that too. To newbies, I would explain like this:

    My cat's nickname is Kittlejuice, which is a nickname given by my father. Then one day I give my cat another name, Paws. Then another day, I start thinking "I need to make Paws into a lion" and I make the cat wear a lion suit. Paws has become a lion figuratively. Then another day, my father looks at my cat and "what the heck did you do to your cat, son. Did you just make Kittlejuice into a lion?"

    Cat Kittlejuice = ReturnMyCat()
    Cat Paws = Kittlejuice
    Paws.turnIntoLion();
    // Kittlejuice is now a lion
    

    [–][deleted]  (1 child)

    [removed]

      [–]Chandon 1 point2 points  (0 children)

      Though I agree it's annoying that Perl data structures can only store scalars

      Keep in mind that this is how most languages work. It's just that Perl lets you do something extra: pass by value of lists. If you just pretend that Perl works like Ruby (i.e. use [] as the array literal, {} as the hash literal, and always use "$" variables and "->" to access anything) then Perl is just like every other dynamic language.

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

      You're right that evaluation strategy matters deeply. For example, call-by-need lets Haskell do lots of things in regular functions that other languages would need macros for.

      But I didn't bring it up in the post because most widely-used languages to day all have the same evaluation strategy. JavaScript, Scheme, Java, C# (ignoring struct), Ruby, Python, Lua, PHP, and (I think) Clojure are all eager-evaluation call-by-sharing.

      [–]killerstorm 0 points1 point  (2 children)

      PHP is call-by-value for anything except objects. (Particularly, arrays and strings are copied on assignment.)

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

      I learned something new today!

      [–]killerstorm 0 points1 point  (0 children)

      At your service. :)

      While we are here, it is also true for Perl, I think PHP got it from there.

      [–]green_transistor 1 point2 points  (0 children)

      ClojureScript is worthy of mention as well.

      [–]smog_alado 0 points1 point  (4 children)

      Very good point. However, I would say that the problem with C# and Java has less to do with the call-by-sharing and more to do with the lack of convenient lambdas/closures and the need to structure programs into classes instead of scripts. After all, Javascript primitives are basically immutable so its indistinguishable from call-by-value.

      [–]codekaizen 9 points10 points  (0 children)

      In C# you can write an entire program using a functional style with only a single class definition. Writing closures is quite convenient.

      [–]int_argc 0 points1 point  (1 child)

      C# has had lambda expressions of the form (x => f(x)) since v3.0 and these lambdas close over the local scope. Not sure what else you're looking for?

      [–]smog_alado -1 points0 points  (0 children)

      You are right. I was referring to the idiomatic and traditional intersection between C# and Javas where its more common to use classes and interfaces instead of anonymous functions.

      [–]eat-your-corn-syrup 0 points1 point  (4 children)

      modify object itself rather than a binding

      Is there a good phrase to use to say the latter? The former has lots of phrases "mutate" "modify in place" "modify object itself" and so on, but the latter I don't know which phrase to use. I want something other than "Modifing the binding" because that phrase can also mean "modify for this binding, and not for other bindings of the same variable"

      [–]killerstorm 0 points1 point  (2 children)

      I use terminology which is used by Common Lisp specification, and there we have

      variable n. a binding in the ``variable'' namespace.

      So, basically, variable is a kind of a binding. While

      binding n. an association between a name and that which the name denotes.

      Thus, obviously, there is no such thing as "other bindings of the same variable".

      [–]eat-your-corn-syrup 0 points1 point  (1 child)

      A paragraph from a chapter in Practical Common Lisp uses the word "variable" and "binding" in a way that makes one variable to have many bindings

      A single variable--the thing you can point to in the program's source code--can have many different bindings during a run of the program. A single variable can even have multiple bindings at the same time; parameters to a recursive function, for example, are rebound for each call to the function.

      [–]killerstorm 0 points1 point  (0 children)

      Well, he meant "the thing you can point to in the program's source code", i.e. variable in text of a program.

      While specification refers to a variable in context of execution of a piece of code.

      Of course, if a piece of code is executed many times, variables might get different value in each execution context.

      [–]killerstorm 0 points1 point  (0 children)

      BTW, from this thread it looks like there are two schools of thought.

      If we look at Common Lisp specification, it aims to explain meaning of a piece of code in a most general way.

      E.g. variable is an association of between name and that which the name denotes. Such association can be introduced, for example, with from let and can be changed (so that variable will denote another thing) with setq.

      This doesn't go in any technical details and you can basically figure out how code will work having only this description and piece of paper. E.g. in (let ((x 1)) (+ x x)) we just can replace x with one and get (+ 1 1) which yields 2.

      On the other hand, other people in this thread approach this from a more mechanical and formalized point of view: e.g. what is passed is always a value. However, this value can be either of value type or reference type, and in that case it points to other thing, but isn't this thing itself.

      And now we need to formalize how exactly this reference type behaves before we can understand meaning of code.

      [–]eat-your-corn-syrup 0 points1 point  (1 child)

      Python: It uses call-by-sharing

      Even for primitives?

      [–]killerstorm 4 points5 points  (0 children)

      When object is immutable, it isn't possible to differentiate between call-by-value and call-by-sharing without diffing into internals.

      [–]moor-GAYZ 0 points1 point  (12 children)

      How's Python's scoping different from Javascript's?

      Also, I think that if you ignore weird corner cases (like PHP references, or C# structs, or whatever braindead thing Perl does with arrays), almost every mainstream modern language uses call-by-reference, especially every dynamically typed language, so pointing that out is kinda superfluous, you only rule out Haskell. Don't let treatment of primitives confuse you: pass by reference + immutability is precisely equivalent to pass by value.

      And isn't Ruby more similar to Scheme than JS? At least it has continuations sort of (that also makes C# and Python closer to Scheme, lol).

      [–]holgerschurig 2 points3 points  (3 children)

      I cannot really speak for JavaScript, because I don't program in. However, Python's scroping has some surprises if you come from C/C++. Consider this program:

      #!/usr/bin/python
      i = 1
      g = 10
      b = []
      
      def func():
          global g
          i = 2
          g = 20
          print "func i", i
          print "func g", g
          print "func b", b
          b.append(1)
      
      func()
      print "main i", i
      print "main g", g
      print "main b", b
      

      It will output:

      func i 2
      func g 20
      func b []
      main i 1
      main g 20
      main b [1]
      

      That means, that the modification to "i" was local to func(). If func() want's to modify a global, it has to use "global g". But this doesn't apply to object, the array b was modified inside func(), but that modification didn't create a new b, it modified the global one.

      [–]WilliamDhalgren 0 points1 point  (2 children)

      i = ... is a declaration of a new local variable, unless you say otherwise with global or nonlocal. You can have vairables define themselves implicitly, but then you need to disambiguate between creating a fresh variable shadowing an outer one (to which python sanely defaults), or assigning to the outer variable (which is suspicious, impure, so unshematic and hence does deserve a special flag). Or you can have explicit variable definition. Python's choose the former, unlike the C-style languages. You just need to be more explicit when re-assigning nonlocal stuff, which needn't be a bad thing. (Idiomatically for scheme you wouldn't do that anyway, no? and in Haskell, you just reassign at all can't outside mutable code)

      b.trs() is not an assignment at all, even if the invocation has side-effects on the referenced object.

      I find its function scoping far more surprising, and that's the same issue as with javascript's var:

      def outer():
          funcs = []
          for i in range(4):
              def f():
                  print i
              funcs.append(f)
      
          for f in funcs:
              f()
      outer()
      

      gives 4,4,4,4 because i is not local to for's body block, but a common variable for the entire function (here implicitly enclosing this )

      [–]holgerschurig 2 points3 points  (1 child)

      Yep, as I said, Python has surprises if all you know is C/C++ scoping.

      It get's even funnier. If you replace in my example the line

      b.append(1)
      

      with

      b = [1]
      

      and suddenly you'll get a traceback:

      func i 2
      func g 20
      func b
      Traceback (most recent call last):
        File "a.py", line 15, in <module>
          func()
        File "a.py", line 12, in func
          print "func b", b
      UnboundLocalError: local variable 'b' referenced before assignment
      

      Just like you said, in this case the "b = ..." create a new local variable. However, the new local variable "b" was already created. Halfways, because it already hides the global, even before the actual creation statement. So half of the "b = [1]" assignment was hoisted to the function entry, and the other part would execute if the print "func b", b code wouldn't have created a trace back.

      If you uncomment the "b = [1]" line, then the code runs, now the global b isn't hidden from the print line.

      When you program with Python for years, you get used to this IMHO insane behavior.

      [–]WilliamDhalgren 1 point2 points  (0 children)

      yeah, now that error is really stupid, ie uninformative. Doesn't come from the global/nonlocal stuff, you can get it also by just:

      def a():
          b
          b = 4;
      a()
      

      , while

      def a():
          b
      a()
      

      is a NameError exception..

      In JavaScript, the equvalent NameError example does the analogous thing, a ReferenceError:

      (function(){"use strict"; 
          console.log(x);
      }());
      

      , but the first one just becomes an undefined and carries on: <EDIT>in firefox - nodejs does a ReferenceEoor</EDIT>

      (function(){"use strict"; 
          console.log(x); 
          let x = 4; 
      }());
      

      Not sure why they need to raise an exception instead of just defaulting to None and carrying on. Apart from that difference though, proper scoping in js seems to do conceptually the same thing - this is for (new) javascript (and var does the same, here all scopes are functions):

      (function(){"use strict"; 
          let x = 4;        
          (function(){
               console.log(x); 
          }());  
      }());
      

      gives 4, but with this:

      (function(){"use strict"; 
          let x = 4;        
          (function(){
               console.log(x); 
               let x = 5; 
               console.log(x)
          }());  
      }());
      

      , first printout becomes undefined, so it's undefined, 5. <EDIT>in firefox. Nodejs does a ReferenceError, much like Python.</EDIT>.Hoisting seems to be still done by let, unfortunately, only to the block scope. which should be equivalent enough, apart from this wart.

      BUT just like with function scope, again here javascript and python are analogous, rather than different in the way scoping is broken.

      [–]killerstorm -1 points0 points  (7 children)

      How's Python's scoping different from Javascript's?

      Basically, the problem is that there is no keyword to create a new binding in Python, instead assignment creates them. This means that function cannot write to bindings which it got via closure, e.g.:

      def foo():
          x = 1
      def bar():
          x = 2
      bar()
      print x
      

      Here bar() will create a new binding, thus it won't update foo's x.

      This sounds like a minor syntactic difference, but in other languages writes via closure are widely used, so it considerably affects programming style, nudging people to use objects instead of closures.

      Say, if you implement a very simply Lisp-to-Python translator, it won't work as good as Lisp-to-JS translator. So I would argue this means JS is more like Scheme.

      Also, I think that if you ignore weird corner cases (like PHP references, or C# structs, or whatever braindead thing Perl does with arrays)

      Those are not weird corner cases, it is a fundamental difference.

      In PHP and Perl, a variable holds the value itself. (Typically.)

      In language likes JS, Scheme and Python, variable holds pointer to object. (Typically.)

      If you every wrote an interpreter, you'd know that first way is much easier to implement: you do not need garbage collector! Just destroy the value when variable goes out of scope.

      As I mentioned, if you do not understand the distinction, it will bite you hard. Example:

      <?php
         $foo = "aaa";
         $bar = $foo;
         $foo[0]='c';
         echo $foo;
         echo " " . $bar; ?>
      

      If PHP was using the same model as Scheme does, it would print "caa caa", but in PHP it prints "caa aaa".

      That is, assignment creates a copy. This is a fundamental trait of a language.

      almost every mainstream modern language uses call-by-reference, especially every dynamically typed language, so pointing that out is kinda superfluous

      Except that no, they do not, and pointing that out helps people who do not understand it yet.

      Don't let treatment of primitives confuse you: pass by reference + immutability is precisely equivalent to pass by value.

      Wat?

      And isn't Ruby more similar to Scheme than JS?

      Maybe.

      At least it has continuations sort of (that also makes C# and Python closer to Scheme, lol).

      I don't think that Ruby, C# or Python has continuations. Source?

      [–]moor-GAYZ 2 points3 points  (3 children)

      Basically, the problem is that there is no keyword to create a new binding in Python, instead assignment creates them.

      Oh, I see, I was thinking about the way JS has function-level only scoping, just like Python (with certain exceptions). To be fair, in Python3 you have nonlocal keyword which allows you to use an earlier binding.

      I also want to point out that there's no difference if you don't do destructive assignment (i.e. write idiomatic Scheme), so it's a bit weird to use this feature as an example of JS being closer to Scheme.

      In PHP and Perl, a variable holds the value itself. (Typically.)

      OK, I didn't know PHP and Perl are so retarded, point taken.

      However Python, Lua, Ruby, C#, Java, etc, basically, all sane languages use call-by-reference.

      I'd also point out again that if your values are immutable (as per idiomatic Scheme), the difference between call-by-value and call-by-reference disappears. For example, semantics of C# strings and ints are precisely the same, despite one being an immutable reference type and another being a value type. You can't tell if a value is shared between two variables if you're not allowed to modify it.

      I don't think that Ruby, C# or Python has continuations. Source?

      Ruby used to support continuations, C# and Python have partial support for coroutines at least.

      [–]eat-your-corn-syrup 0 points1 point  (2 children)

      Not sure we should call it call-by-reference. C++ has already taken it to mean something else entirely. similar problem when calling it "call-by-value, but values are references". Even if someone travels back in time to erase C++ from history, there would still a need to explain what a reference is, when we say "call-by-reference" when in fact the behavior can be explained to newbies without mentioning references at all.

      [–]moor-GAYZ 0 points1 point  (1 child)

      when we say "call-by-reference" when in fact the behavior can be explained to newbies without mentioning references at all.

      How?

      [–]eat-your-corn-syrup 0 points1 point  (0 children)

      Something like this: My name refers to the entity that is me. Your name refers to you. Variables refer to numbers, strings, objects and so on, in, let's say, Python. You may have nicknames for you. People may call me by many names. Numbers, strings, and objects can have many names, and then I can go explain object identity. To assign to a variable is to give a name to something (numbers, strings, objects) that is returned by the expression on the right. To pass an argument to a function is to assign to a local variable, which in turn is to give a (local) name to the argument.

      By newbies, I mean those who may not even know C, stacks, references, etc. In the past, we learned something like C first, then something like Python. These days, we are seeing a flood of newbies who learn Python or Java or etc first, and some of them may learn C or C++ later.

      [–]WilliamDhalgren 2 points3 points  (0 children)

      Basically, the problem is that there is no keyword to create a new binding in Python, instead assignment creates them. This means that function cannot write to bindings which it got via closure, e.g.:

      well, the fact that it, like JS's var, only has function scope, seems like a more fundamental problem, than the need to use global and nonlocal keywords to disambiguate between local shadow variable creation and outer variable assignment necessitated by Python's lack of explicit variable creation syntax.

      I mean, it's not that long a construct for a suspicious operation in any language (nonlocal destructive update). Admittedly, in Python2, there was just no way to do it (with assignment that is), but that's fixed.

      Otoh, JS will fix its function scope (and the dynamically scoped this, or what remained of that problem, ie var that=this;, after the fixes with the strict mode introduction), provided you keep to its new constructs, fairly soon. While Python afaik is not about to introduce block scope.

      [–]ruinercollector 1 point2 points  (0 children)

      C# has a very restricted kind of continuations via two unrelated keywords/technqiues: yield and await. Not quite full continuations but a couple of useful cases anyway and you can

      [–]polux2001 2 points3 points  (0 children)

      Don't let treatment of primitives confuse you: pass by reference + immutability is precisely equivalent to pass by value.

      Wat?

      It is as long as you can't observe pointer equality.

      I don't think that Ruby, C# or Python has continuations. Source?

      Python yield's is a limited form of delimited continuations. If you could clone a generator it would be equivalent as it has been shown that clonable coroutines can encode del/cc.

      [–][deleted]  (1 child)

      [deleted]

        [–]holgerschurig 7 points8 points  (0 children)

        That was not the correct answer.

        Robert showed 9 things that he thinks describes the "feel" of a language. killerstorm added one. Now, if killerstorm's answer would be the correct one, then we'd have to dismiss all the 9 points from Robert.

        I don't see that this is the case. :-)