Advertisement

Scripting Language Genesis

Started by November 01, 2004 07:28 AM
116 comments, last by cmp 20 years, 2 months ago
Quote:
Original post by cmp
could you explain what closures really are?


Sure.

Edit: This is wrong. A closure is the block of code that is called, it is a method of sorts and can reference variables outside it's scope - sometimes even outside the method that it was defined in's stack frame. Closure Definition

Basically they are method acceptors. You define little blocks of code to be called by the closure. For example say you want to do something with each value in a list you can say:
list a = [1, 3, 6]a.each    // now this code gets called for each element in the list with value    // being the value of the element    system.println(value)

The output would be:
136

As you can see this is easier than using iterators or other ways to move through a list. Even if I just implemented the each closure only, it would be a great addition.

For the map closure on a list, the inner code needs to return a value to put into the new list.
list a = [1, 3, 6]list bb = a.map    // this code also gets called for each value in the list,    // but it returns a new value that will be put into list b    return value * 3

List a now equals [3, 9, 18].
There is also the filter closure, The bit of code also gets the value, but it returns a boolean whether to include that value in the returned list.
list a = [1, 2, 3, 4, 5, 6]list blist b = a.filter    // this code gets called with the value, and returns a boolean    // whether to include it in the returned list    return value < 4

List b now equals [1, 2, 3]. Closures are a great way to implement iterations, and mapping functions. If anyone has used Haskell, they will find that it has this functionality as well. I think Ruby also has these blocks of code.

The good thing about closures is that the code can also reference other variables outside the closure scope. For example a sum is very easy to calculate.list a = [1, 3, 6]
integer sum = 0
a.each
sum += value

I was even thinking about having for and while loops, if statements and other code branching keywords as closures, that it the C++ implementation of them does the actual branching - the voodoo language (at least the bytecode) doesn't need to have branches or loops. You could say something like:
system.if(x < 7)    x = 7


And the if closure is defined as:
class system    + void if(boolean b){void()}


Quote:
update: i've released the micro xml to xml converter.


Cool, I will have a look next time I'm on the internet (56k, and calls can't get through).

Quote:
Original post by visage
for your 1...5 argument, I would recommend you do math notation.

Will look into it. At the moment though that would be parsed a bit wrong, unmatched braces, but it looks like a good syntax.

I shy away from mathematics a bit because of useability, maths isn't the easiest thing to learn, and a lot of things that get explained using mathematics could be explained easier other ways. I'm thinking turing machines in particular, have you tried learning them straight from their mathematical notation? Our lecturer tried to teach us that way, it just doesn't work. It is often easier to use english.

But with that aside, that notation is parse-able and looks good. I would still argue it's necessity though, do we need inclusion / exclusion in a non-maths specific language?

[Edited by - umbrae on November 16, 2004 1:07:42 AM]
I think you have sequences wrong. There is another way to do them, which I think has wider applications, and that is to use generators.



A generator is a kind of function which yields a (possibly) new value each time it called until it runs out of values (assuming it runs out of values).
function start .. end  for i = start to end    yield il = 0 .. 10

Now, 'l' isn't the list 0..10, but a generator that returns 0 to 10 in turn. The language would be designed so that 'genuine' sequences are indistinguishable from generators.
l1 = 0 .. 10l2 = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]print l1[5], l2[5]

This outputs 5 in both cases.



Closures and generators could be combined to make things rather spiffy.
l = (0 .. 10) * 7

In this example, it could either turn the generator into a list and then multiply it. Or, it could turn the generator into a closure that generates its value and multiplies it by 7. i.e. It would be equivalent to:
functions iota_mul (start, end, x)  for i = start to end    yield i * xl = iota_mul(0, 10)

This would be particularly handy if the list never ends:
// Generate all the natural numbersfunctions naturals  int i = 0  while true    yield ++i// If naturals must be converted to a list, the program will// crash here. If it's wrapped in a closure, the program will// not crash.evens = naturals * 2// The first 'c' elements of 'l'function first (int c, list l)  list r  for i = 0 to c    r.append(l)// First 100 evens.first(100, evens)
CoV
Advertisement
Quote:
Original post by umbrae
But with that aside, that notation is parse-able and looks good. I would still argue it's necessity though, do we need inclusion / exclusion in a non-maths specific language?

I think that almost every programmer would think that '[ 1, 2, 3 )' was a syntax error. It still hurts my head when I see it in mathematical notation.



I am quite certain that '...' is inclusive.
CoV
Quote:
Original post by Mayrel
Erm. That's not a closure. That's a function that accepts a function as a (hidden) argument.

Good point, but isn't that basically what a closure is? The only difference is that the function that is passed can be in code. Closures are implemented by the compiler, they are treated differently because they can reference variables outside the closure code. You also need to say what the method that you are calling takes as parameters and what is returns.

Quote:
If you must have explicit typing:

I'm a little confused, why do you need the i:int -> int when you are calling the closure?

Quote:
Original post by Mayrel
I think you have sequences wrong. There is another way to do them, which I think has wider applications, and that is to use generators.

Generators sound a lot like co-routines, is this about right?

They do sound really cool, but I am a little hazy on their application, what would be a practical use for them in a scripting language? (aside from mathematics and natural numbers)
Quote:
Original post by umbrae
stuff
closures: A better definition.

You have it confused slightly.

'if' and 'each' are not the closures, they are ordinary functions. the closures are the things being passed to these functions.

Closures are the combination of a function and an environment. So
Quote:
The good thing about closures is that the code can also reference other variables outside the closure scope.
is only slightly correct. It's not the "good thing" about them, it's what they are.

Scheme is an excellent language to look at for how closures work. Smalltalk I've also heard good things about.
Quote:
Original post by umbrae
system.if(x < 7)    x = 7


Why bring 'system' into this. Noting that 'if' only makes sense when applied to something of boolean type, I conclude that 'if' should be encapsulated with the boolean type.
(x<7).if  x = 7

Or, we can say that a boolean is a function that calls its argument.
x < 7  x = 7

Then we'd define the special function boolean.call as
boolean.call(true, function f) = fboolean.call(false, function f) = false

Obscenely concise, no? [wink]
CoV
Advertisement
Quote:
Original post by C-Junkieclosures: A better definition.

You have it confused slightly.


I did.

I knew I wasn't referring to 'real' closures, but I also had closures a bit confused.

The closures I was thinking of are a bit like a reversed closure, the closure is defined in the code, in a certain method and has to be called by a method that is defined as a closure calling method. They are a cut down closure - I'm not sure if I want to handle funcitons referencing variables that don't exist any more, sounds like a can of worms to me. So the function that is being called is 'stuck' in the method it is defined in, and the fucntion that calls it has to be defined there and then. Also the method is called there and then, it can't be called anytime.

I will have method objects that point to methods, and can be called using some syntax that I haven't worked out yet. These will replace the other funcitonality that closures provide, comparison methods etc.

Quote:
Original post by Mayrel
Why bring 'system' into this. Noting that 'if' only makes sense when applied to something of boolean type, I conclude that 'if' should be encapsulated with the boolean type.

I completely agree, it was just a fast example.

Would you like a language that had no if statements? Just these boolean closure type things?
Quote:
Original post by umbrae
They are a cut down closure - I'm not sure if I want to handle funcitons referencing variables that don't exist any more, sounds like a can of worms to me.
Indeed. The language 'D' does half-hearted closures like this. It requires that the scope still exist so it doesn't have to deal with that "can of worms."
Quote:
Original post by umbrae
Quote:
Original post by Mayrel
Erm. That's not a closure. That's a function that accepts a function as a (hidden) argument.

Good point, but isn't that basically what a closure is? The only difference is that the function that is passed can be in code. Closures are implemented by the compiler, they are treated differently because they can reference variables outside the closure code. You also need to say what the method that you are calling takes as parameters and what is returns.

No. As C-Junkie says, the closure is the function being passed.
Quote:

Quote:
If you must have explicit typing:

I'm a little confused, why do you need the i:int -> int when you are calling the closure?

You probably wouldn't. But I was being explicit.
Quote:

Quote:
Original post by Mayrel
I think you have sequences wrong. There is another way to do them, which I think has wider applications, and that is to use generators.

Generators sound a lot like co-routines, is this about right?

Sort of. A generator is a higher-level construct, probably built on top of coroutines. In my examples, I did indeed use coroutines.
Quote:

They do sound really cool, but I am a little hazy on their application, what would be a practical use for them in a scripting language? (aside from mathematics and natural numbers)

Hmm. They work best when mixed with a number of other features. The programming language Icon does this particularly elegantly.



I'll try to represent it in a C++-like language.
// This is all the actors in the scene.extern list<Actor> actors// This index form is a generator that returns the elements of// the list that satisfy the condition. In these kinds of // expressions, . refers to the <i>context object</i> (rather// like Pascal's <i>with</i>)// In this case, it only returns the first element found.a = actors[.name ~= /foo/]// 'every' evaluates its argument until all of its generators// are exhausted, and collects the values in a list.// So in this case, we get a list of every actor which// satisfies the condition.a = every actors[.name ~= /foo/]
CoV
Quote:
Original post by umbrae
Would you like a language that had no if statements? Just these boolean closure type things?

No. But I would like a language that had 'boolean closure type things' in the 'core of the language', and 'if' syntax provided in terms of that.
CoV

This topic is closed to new replies.

Advertisement