fun fun fn

Functions are essential to any high level language. Without them, it’s very tedious (and error prone) to do anything. They may be disguised in various ways, being referred to as “sub-routines” or initiated using peculiar keywords like “def” and “defn”, but it’s all the same idea. The difference is often power.

Programmers have debated the differences between functions and closures. The two ideas overlap so much, especially in certain programming languages, that one might mistake them for being the same. As far as I can tell, closures can be used to simulate functions but functions aren’t always capable of simulating closures, depending on the language you are talking about.

The function structure of JavaScript – that functions are objects – seems to be the logical zenith for imperative programming styles. Why not give functions all the powers of objects? In some senses, the implementation of this model has been, in my opinion, somewhat messy. There is a beautiful side of JavaScript, as Douglas Crockford would argue. I see the problem not as a syntactic one but as one due to the cross-referencing that leads to memory leaks. The style of JavaScript and the availability of engines from processing it are the two great factors – in my opinion – as to why the language has exploded in usage and resulted in a number of spin-off languages. While not having explored all of them, I wonder if most of these are really not much more than libraries to fill in the gaps that browsers have either not implemented or implemented poorly. Two of the most obvious improvements I noticed brought by Node.JS were for file and string handling. The basics of the language itself, however, remain mostly unchanged, which probably says something about either the engines used (perhaps Chrome’s V8 complexity, as V8 was used for Node.JS) and/or the desire of the sub-language creator to maintain that syntax.

Back in my early days of developing Goldfish, my design of virtual functions went from being a keyword-stimulated execution-body search (making function headers a fancy goto-label) to being wholly-self-contained objects. I can’t recall if I ever completed the design before I scratched the project after realizing the object model wasn’t going to work out. During that time, I left the C-style programming world and made all my code object-oriented. Needless to say, that has also influenced my perception of programming languages in general.

Functions in Copper are a function-object hybrid. Within the virtual machine (VM), they are classes that contain both a robin hood hash table scope and a list of tokens that act as the “execution body” of the function. This makes processing within the VM convenient and may (big MAYBE) allow for safe multi-threading without mutex locks (depending on implementation). It allows extensions to save callbacks or even add their own created by another virtual machine. Within the language of Copper itself, it allows for functions to play a dual role of being both the objects (containing members) and functions. With the appropriate functions, it allows for these functions to act like Python dictionaries. Alittle coding and you can also simulate Python generators.

Enough talk of this marvelous little object. What does it look like?
fn
That’s it. That’s all that is needed to create a function. “fn” was chosen because it’s short. Ok, there is more. Below is an example of creating a function and assigning it to a variable.
a = fn() {}
Variables only have one type and they are the only non-keyword words, so there is no need for a “var” or some declaration keyword for creation. They are created as soon as they are named.

Naturally, functions can contain accept parameters and contain operations. Their persistent scope members can be accessed by using the period (“.”).

a = fn(p) { p.b = true }

To make this language safe, all variables an instantiated with a function. That way they immediately have their own scope and empty execution body. There is a built-in function to check for this “blank slate” state because it also happens to be the default return for every function.

Parameters are passed by reference (and a comma separation is optional, not required), but the assignment (and pointer assignment) operators delink the parameter variable from the variable it points to. The pointer assignment operator will be discussed in the next installment, but just know that it can be used anywhere the assignment operator can be used. The difference is that it creates a pointer rather than forces a copy.

There is one other type of parameter functions can take: An assigned parameter that instantiates a member of the persistent scope of the function, rather than being a parameter that must be initiated when the function is called.

a = fn( my_persistent = 12 ) {}

This means the variable can be accessed in the same way that all other persistent scope variables are accessed. The beauty of this is both in its convenience and in the fact that there are places where the alternative (assigning each member via the member-access token (“.”)) won’t do the job.

Here is a very simple routine:

err_if_empty = fn(value) {
assert(not(are_empty(value())))
}

Values passed into functions are saved in the parameter variables, which are functions, so it doesn’t matter if the parameter value was an object and not a function – you still must get the data the same way as though it were a function.

To get data from a function, you call it.

a = fn { ret(5) }
print(a())
a = 5
print(a())

(Note: there is no “print” function – you must add it – but there is an extension I made for the job.) In the above examples, the result is the same, but the second example has a direct assignment, which allows the virtual machine to avoid running an execution body and simply return a value. Because of this, Copper is a kind of pseudo-dynamic language. While Copper is technically statically-typed (there is only one type variables can hold: function), the function execution bodies can be short-circuited. Rest-assured, the variable being assigned a direct value can still access persistent scope members and be treated like every other function.

The assigned parameters and call-instantiated parameters can be randomly arranged in the parameter body of the function, in case that wasn’t clear.

a = fn( ci ap=10 cii ) {}

At the current time, there are no private variables. JavaScript doesn’t have a “private” label, but at least you can simulate it. Copper, however, doesn’t even allow for simulation. Hence, it could be readily said that Copper doesn’t allow for Closures. There are a number of good reasons for this, which I intend to discuss later, but one I have already spoken about is memory management. Without going into great detail, it should suffice to say that the ownership model of Copper is monolithic. A single variable owns a function so that memory can be cleaned up, and both of these combined lead to the privacy label being a meaningless restriction. Exactly what would it do? And how? Consequently, the design allows for accessing every persistent scope variable from the global scope.

Despite not having closures, generators can be simulated to some degree. There is not “yield” keyword, so giving a function generator-functionality is not as easy as in Python, but since state can be preserved, creating a generator can be as simple as using an assigned parameter (initiating a persistent scope member to the desired start value).

getRandom = fn(seed=831980) {
this.seed = int_mod(int_mul(this.seed() 3.8192) 109174)
ret(this.seed())
}

Admittedly, sometimes you will need wrappers, which requires more explanation about the class structure of Copper. The object oriented paradigms in Copper require two special keywords: “this” and “super”. I will discuss those in a future article.

Within the virtual machine, functions are deep-copied for assignment. In my opinion, this is so much safer than creating a shallow copy just to preserve memory. If you come from a C or C++ background, it’s what you expect. If you come from a Java background, you would expect shallow copies, which makes sense for that language. In Python, you have copy.deepcopy(), which makes even more sense for that GC language (and I wish Java had that). Copper and Rust share this emphasis on memory protection. But never fear! In Copper, you can create pointers and those won’t deep copy.

In addition to memory safety, it’s also faster to perform a deep-copy of functions in the virtual machine than increase complexity to work around it when in Copper you can use pointers. I did consider allowing the user to implement their own “copy” functions and use shallow copying to perform deep copying, but it turned out to be impossible to make this work in practice. Fortunately, it only took me a small mental experiment and writing out some hypothetical code to figure that one out. *phew*

Conclusion

I’ve covered a lot of ground very quickly. I’ve created a more complete guide to Copper functions that I will post later, which is actually rather short but presumably more clear, but I’d like to make that one of the last posts in my introduction to the VM mechanics since it is likely to be the one more people look for.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s