Bay 12 Games Forum

Please login or register.

Login with username, password and session length
Advanced search  
Pages: 1 2 [3]

Author Topic: The programming language thread.  (Read 5055 times)

Virex

  • Bay Watcher
  • Subjects interest attracted. Annalyses pending...
    • View Profile
Re: The programming language thread.
« Reply #30 on: May 31, 2011, 04:51:10 pm »

Because everyones "I want to make a game" thread gets derailed at some point by people talking about programming languages I made this thread to allow an outlet for that and keep peoples game threads on topic.

I am an expert on Java, C# and PSQL with a moderate to high level of familiarity with dozens of other languages, so I will contribute what I can.

I don't really know what language you'd consider more serious then C#? It's fully object oriented, functional and structured. Hell, it even has multimethods, a metaobject protocol and closures when you need them, things that "crowd favorite" C++ lacks. (Ok, granted, it lacks a preprocessor and it's not extensible, but then again, most languages aren't)

This isn't completely true. C# does not have closures at all. It has things incorrectly called closures that are really just inline anonymous methods, but they lack absolute encapsulation and are therefore not actual closures.
I'm not a fan of C# Closures, Delegates, Predicates, and all the crap they keep shoe-horning in... it just feels so "inelegant."
That's probably because they're trying to fit those things in a rigid class-based system that doesn't lend itself too well to those kinds of things. It's still better then for example C, where you can't even get an anonymous method easily.

Quote
I know I'm probably going to be assaulted for this, but I actually like how JavaScript (well, ECMAScript) does a lot of things, including closures.  There are some pitfalls to it, but I think it's an overall fairly well thought out language.  I'm actually very interested in the advancements being done with JS currently but I worry that it doesn't have that much further to go before things really slow down in the VM front.
I don't think the VM is really that much of a problem. For example Java and Forth use virtual machines and the difference between those languages and C++ is within the margin where coding style matters more than the intrinsic speed of the language (Ignoring the startup time for the JVM for a moment). The biggest bottleneck is probably that it's interpreted, but that's such a fundamental part of the language that it's tough to switch to a different style.

Quote
I've only began learning Lisp.  I've kind of dropped the ball recently with looking for a house.

Common Lisp is a pretty interesting language, but it's dearly lacking in freely available libraries. There seem to be quite some libraries that serve as prototypes for new techniques (not strange, considering it's roots), but there doesn't even seem to be a decently documented, finished GUI toolkit, other than hand-rolling bindings for C libraries.
« Last Edit: May 31, 2011, 04:54:22 pm by Virex »
Logged

Nadaka

  • Bay Watcher
    • View Profile
    • http://www.nadaka.us
Re: The programming language thread.
« Reply #31 on: May 31, 2011, 05:42:04 pm »

In some cases, java has an advantage in speed over c++. For instance instantiating an object on the heap in java is about 3 times faster than c++.

I again object to things that are called closures that really are not closures. Javascript "closures" are not fully encapsulated and are not real closures.
Logged
Take me out to the black, tell them I ain't comin' back...
I don't care cause I'm still free, you can't take the sky from me...

I turned myself into a monster, to fight against the monsters of the world.

Andir

  • Bay Watcher
    • View Profile
Re: The programming language thread.
« Reply #32 on: May 31, 2011, 06:48:54 pm »

I again object to things that are called closures that really are not closures. Javascript "closures" are not fully encapsulated and are not real closures.
I think you may be confusing what a closure is... a closure, by definition, has scope to all it's local variables and any within it's parent environment.  In JavaScript, "upvalues" include the parent object's closure.  This, of course, recursively gives you access to global values.  What language would you say has "true" closures by your definition?  You can't really "fully encapsulate" a closure since that would make them fairly worthless.  Maybe you are thinking of monads?  Sorry, not monad... I can't even think of something to explain that.
« Last Edit: May 31, 2011, 07:02:15 pm by Andir »
Logged
"Having faith" that the bridge will not fall, implies that the bridge itself isn't that trustworthy. It's not that different from "I pray that the bridge will hold my weight."

Nadaka

  • Bay Watcher
    • View Profile
    • http://www.nadaka.us
Re: The programming language thread.
« Reply #33 on: May 31, 2011, 07:37:50 pm »

I again object to things that are called closures that really are not closures. Javascript "closures" are not fully encapsulated and are not real closures.
I think you may be confusing what a closure is... a closure, by definition, has scope to all it's local variables and any within it's parent environment.  In JavaScript, "upvalues" include the parent object's closure.  This, of course, recursively gives you access to global values.  What language would you say has "true" closures by your definition?  You can't really "fully encapsulate" a closure since that would make them fairly worthless.  Maybe you are thinking of monads?  Sorry, not monad... I can't even think of something to explain that.

Not confused.

A closure only has access to local variables and any values specifically passed in (and you can not pass in references, only values). It has no access at all to any thing defined in any other scope. The only language that I am more than passingly aware of that correctly implements closures is LISP.
« Last Edit: May 31, 2011, 07:43:00 pm by Nadaka »
Logged
Take me out to the black, tell them I ain't comin' back...
I don't care cause I'm still free, you can't take the sky from me...

I turned myself into a monster, to fight against the monsters of the world.

Andir

  • Bay Watcher
    • View Profile
Re: The programming language thread.
« Reply #34 on: May 31, 2011, 08:52:59 pm »

I again object to things that are called closures that really are not closures. Javascript "closures" are not fully encapsulated and are not real closures.
I think you may be confusing what a closure is... a closure, by definition, has scope to all it's local variables and any within it's parent environment.  In JavaScript, "upvalues" include the parent object's closure.  This, of course, recursively gives you access to global values.  What language would you say has "true" closures by your definition?  You can't really "fully encapsulate" a closure since that would make them fairly worthless.  Maybe you are thinking of monads?  Sorry, not monad... I can't even think of something to explain that.

Not confused.

A closure only has access to local variables and any values specifically passed in (and you can not pass in references, only values). It has no access at all to any thing defined in any other scope. The only language that I am more than passingly aware of that correctly implements closures is LISP.
Edit: bad example... it's late...

Here:
http://jsfiddle.net/BsPjs/
Code: [Select]
var globalId = 0;
function closure_test (passed) {
  var id = globalId++;
  return function () {
    alert("id:" + id);
    return passed;
  };
}
 
var test1 = closure_test("one");
var test2 = closure_test("two");

alert(test1());
alert(test2());

The only difference I see with Lisp is that it can be "greedy" and bind free variables to itself.  Why you'd want to do that in any other language besides Lisp, I don't know.  Just create a local variable (which I did with id...)
« Last Edit: May 31, 2011, 09:55:17 pm by Andir »
Logged
"Having faith" that the bridge will not fall, implies that the bridge itself isn't that trustworthy. It's not that different from "I pray that the bridge will hold my weight."

Alexhans

  • Bay Watcher
  • This is toodamn shortto write something meaningful
    • View Profile
    • Osteopatia y Neurotonia
Re: The programming language thread.
« Reply #35 on: June 01, 2011, 10:56:36 am »

posting to have it on my "Show new replies to your posts." thingie.
Logged
“Eight years was awesome and I was famous and I was powerful" - George W. Bush.

Nadaka

  • Bay Watcher
    • View Profile
    • http://www.nadaka.us
Re: The programming language thread.
« Reply #36 on: June 01, 2011, 12:00:15 pm »


Here:
http://jsfiddle.net/BsPjs/
Code: [Select]
var globalId = 0;
function closure_test (passed) {
  var id = globalId++;
  return function () {
    alert("id:" + id);
    return passed;
  };
}
 
var test1 = closure_test("one");
var test2 = closure_test("two");

alert(test1());
alert(test2());

The only difference I see with Lisp is that it can be "greedy" and bind free variables to itself.  Why you'd want to do that in any other language besides Lisp, I don't know.  Just create a local variable (which I did with id...)
Now look at the following modification of your code and how it works.
The value read from the globalId variable is not fixed at the time the closure is created. you can tell this because the function displays "id:3", "id:3", "id:4", when it should display "id:1", "id:2", "id:3" if it was fully closed over. Javascript does not have closures.

Closures are important for two reasons. 1: they make the task of mathematically proving an algorithm implemented with them comparatively simple. 2: more practically, they are perfectly thread safe.

Code: [Select]
var globalId = 0;
function closure_test (passed) {
  globalId++;
  return function () {
    alert("id:" + globalId);
    return passed;
  };
}
 
var test1 = closure_test("one");
var test2 = closure_test("two");
var test3 = closure_test("three");

alert(test1());
alert(test2());
globalId++;
alert(test3());
« Last Edit: June 01, 2011, 12:06:06 pm by Nadaka »
Logged
Take me out to the black, tell them I ain't comin' back...
I don't care cause I'm still free, you can't take the sky from me...

I turned myself into a monster, to fight against the monsters of the world.

Virex

  • Bay Watcher
  • Subjects interest attracted. Annalyses pending...
    • View Profile
Re: The programming language thread.
« Reply #37 on: June 01, 2011, 02:55:26 pm »

Erm, they work the same way in Common Lisp (tested with CCL):
Code: (Test one, dynamic scoping of the ID) [Select]
CL-USER> (defvar *id* 0)
ID
CL-USER> (defun closure-test ()
         (incf *id*)
         (lambda ()
        (format t "id: ~a " *id*)))
CLOSURE-TEST
CL-USER> (defvar test-list (loop repeat 3 collecting (closure-test)))
TEST-LIST
CL-USER> (mapc #'funcall test-list)
id: 3 id: 3 id: 3

Code: (test two, lexical scoping) [Select]
(let* ((closure-id 0)
            (closure-list (loop repeat 3 collecting
                           (progn
                            (incf closure-id)
                            (lambda ()
                              (format t "id: ~a~%" closure-id))))))
         (mapc #'funcall closure-list))
id: 3
id: 3
id: 3
If you want to enclose a value in Common Lisp, instead of a variable, you're going to have to shadow the externally defined variable
Code: (Once more, with shadowing) [Select]
(let* ((closure-id 0)
            (closure-list (loop repeat 3 collecting
                           (progn
                            (incf closure-id)
                            (let ((closure-id closure-id)) ;Note, only works on lexically scoped variables, don't use the same name for something you defvar'ed. Another use for those *'s
                              (lambda ()
                               (format t "id: ~a~%" closure-id)))))))
         (mapc #'funcall closure-list))
id: 1
id: 2
id: 3
Of course, we can easely define a macro to do that for us:
Code: (Shadowing made easy) [Select]
(defmacro capture (variables &body code)
"Use this macro around a function definition to have it enclose the value of lexically scoped variables instead of the variables themselves"
         `(let (,@(loop for var in variables collecting (list var var)))
              ,@code))
Which turns the above code into:
Code: [Select]
(let* ((closure-id 0)
            (closure-list (loop repeat 3 collecting
                           (progn
                            (incf closure-id)
                            (capture (closure-id)
                              (lambda ()
                               (format t "id: ~a~%" closure-id)))))))
         (mapc #'funcall closure-list))
Which saves a few keystrokes and makes it clear we're trying to enclose closure-id in our closure (of course you could combine the anonymous function definition with the capture, but that's left as an exercise to the reader or at least someone less lazy then I am)
(Note that this macro will not work if you define the ID using defvar, because that means ID is dynamically scoped. The easiest way to fix that is probably to add a suffix to the local variables. I'm not sure if you can shadow a dynamic variable with a lexical one, but if you can, then that'd be a better option)

There is an important reason that closures don't capture values instead of variables, because if they did they would behave differently from normal functions, which brings in it's own set of headaches
(not to mention that you can't have the classic lisp example:
Code: [Select]
(let ((counter 0))
         (list (lambda ()
               (incf counter))
             (lambda ()
               (setf counter 0))))
which defines two closures sharing the same variable, one giving you the next integer (for numeric ID's or something) and the second being usable to reset the counter.)


Edit: Noticed I was using mapcar and mapc interchangeably. Cleaned that up to avoid confusion and because mapc is more appropriate in this case.


Edit 2: Figured out one way to make capture work with dynamic variables:
Code: [Select]
(defmacro capture (variables &body code)
`(let (,@(loop for var in variables collecting (list (intern (concatenate 'string "LOCAL-" (symbol-name var))) var)))
              ,@code))

By getting the symbol-name of the variable that's being captured we can concatenate local to it (needs to be uppercase or else the name will be |local-*| instead of local-*), meaning the value of for example x is bound to local-x. Of course this is under the same reservation as any anaphoric macro (macro's that bind variables for you), namely that you need to make sure you don't get name interference while using it.
« Last Edit: June 01, 2011, 04:51:19 pm by Virex »
Logged

Nadaka

  • Bay Watcher
    • View Profile
    • http://www.nadaka.us
Re: The programming language thread.
« Reply #38 on: June 01, 2011, 04:21:56 pm »

The definition that was drilled into me at university, it was in my books, it was in my tests:
A closure is a function that is stateless and completely enclosed.
A closure may not read any external state except its parameters.
The parameters of a closure may not be or contain references to any outside state.
A closure may not alter any external state except by returning a value.

That is not the same definition you guys are using. Was I taught an archaic definition?
Logged
Take me out to the black, tell them I ain't comin' back...
I don't care cause I'm still free, you can't take the sky from me...

I turned myself into a monster, to fight against the monsters of the world.

Virex

  • Bay Watcher
  • Subjects interest attracted. Annalyses pending...
    • View Profile
Re: The programming language thread.
« Reply #39 on: June 01, 2011, 04:35:51 pm »

I can't say for sure, since I don't have the same level of backing as you do, but it seems to me that you're definition is the formal definition used in algorithm analysis, while most people here use the layman's definition of "A function that encloses non-global data that wasn't passed to it". The formal definition is to this kind of closure what a pure function is to a function. (Note that wikipedia also doesn't say anything about closures needing to be pure and neither do Practical Common Lisp or On Lisp)
« Last Edit: June 01, 2011, 04:40:19 pm by Virex »
Logged

Andir

  • Bay Watcher
    • View Profile
Re: The programming language thread.
« Reply #40 on: June 01, 2011, 04:37:02 pm »

The definition that was drilled into me at university, it was in my books, it was in my tests:
A closure is a function that is stateless and completely enclosed.
A closure may not read any external state except its parameters.
The parameters of a closure may not be or contain references to any outside state.
A closure may not alter any external state except by returning a value.

That is not the same definition you guys are using. Was I taught an archaic definition?
It sounds a bit restrictive and it breaks a lot of what makes closures powerful.  According to the following lisp, the closure has a definite state (storing the value of counter.)  The closure definition even has a starting state (counter 0)

I'm not an expert by any means so there could be a language that implements that definition, but I am unaware of any.

The only difference I see is in semantics... take this Lisp Closure: (from: http://www.psg.com/~dlamkins/sl/chapter11.html)
Code: (clisp in linux) [Select]
[1]> (let ((counter 0))
    (defun counter-next ()
      (incf counter))
    (defun counter-reset ()
      (setq counter 0)))
COUNTER-RESET
[2]> (counter-next)
1
[3]> (counter-next)
2
[4]> (counter-reset)
0
[5]> (counter-next)
1
[6]> counter

*** - SYSTEM::READ-EVAL-PRINT: variable COUNTER has no value


To do the same thing in Javascript you'd likely want to create an object (or object factory) with next() and reset() accessors instead of "polluting" the local scope with two functions... you could create two functions in the local scope (with a "closured" counter) if you wanted to though:
Code: [Select]
(function() {
    var count = 0;
    this.next = function() {
        return ++count;
    };
    this.reset = function() {
        count = 0;
    };
})();

alert(next()); //1
alert(next()); //2
reset();
alert(next()); //1
try {
    alert(count); //undefined
} catch (e) {
    alert(e); // "count is not defined"
}
Which works the same as the Lisp version.
Logged
"Having faith" that the bridge will not fall, implies that the bridge itself isn't that trustworthy. It's not that different from "I pray that the bridge will hold my weight."
Pages: 1 2 [3]