4 ms·
The biggest advantage of scripting for me has been 1. Co-routines Co-routines (co-operative multi-tasking?) mean you can do stuff like while (isWalking(
by greggman 13y ago
The biggest advantage of scripting for me has been
1. Co-routines
Co-routines (co-operative multi-tasking?) mean you can do stuff like
while (isWalking()) {
advance();
yield();
}
This is effectively 'yield' from Python, C#, etc.. You can implement this in C++ by swapping stacks and calling setjmp but there's usually issues.
2. Iteration time
You can usually swap script code live. This project aims to fix that for C++ though I'm a little skeptical it can stay robust
https://github.com/RuntimeCompiledCPlusPlus/RuntimeCompiledCPlusPlus https://github.com/RuntimeCompiledCPlusPlus/RuntimeCompiledC...
- forrestthewoods 13y agoUE4 has hot reloading of C++ code. Some of their demos were pretty impressive. However I'd have to use it for a long period of time on a "real" project to see how reliable it truly is.
- crucialfelix 13y agoThe slightly obscure music programming language SuperCollider [1] added co-routines about 10 years ago and they became one of my beloved techniques. Was very glad to see them come to python and soon to mainstream javascript. Boost has a c++ implementation but it looks quite different: http://www.boost.org/doc/libs/1_55_0/libs/coroutine/doc/html/index.html http://www.boost.org/doc/libs/1_55_0/libs/coroutine/doc/html... [1] http://supercollider.github.io http://supercollider.github.io edit: pythons new asyncio stuff looks very interesting: https://docs.python.org/3.4/library/asyncio-task.html https://docs.python.org/3.4/library/asyncio-task.html
- munificent 13y ago> Was very glad to see them come to python and soon to mainstream javascript. I really need to get around to writing a blog post to explain this in detail since this misapprehension is endemic. Python and JavaScript do not have coroutines, they have generators. Lua has actual coroutines. The latter is dramatically more expressive than what you can do with what Python, JavaScript, and C# offer. This mistake drives me crazy because it means people don't know what they're missing. Here's a quick example. Let's say we've got a little Python class for binary trees: class Tree: def __init__(self, left, data, right): self.left = left self.data = data self.right = right We'll add a method to do an in-order traversal. It takes a callback and invokes the callback for every data value in the tree, like so: def walk(self, callback): """Traverse the tree in order, invoking `callback` on each node.""" if self.left: self.left.walk(callback) callback(self.data) if self.right: self.right.walk(callback) We can create a little tree and then print the data items in order like so: tree = Tree(Tree(Tree(None, 1, None), 2, Tree(None, 3, None)), 4, None) tree.walk(print) (This works in Python 3, in Python 2, you'll have to make a little fn for print.) Swell, right? Later, we decide we want to iterate over the items in a tree. Easy-peasy, Python has generators! We can just make a function that takes a Tree and returns a generator. We already have a method to walk the nodes, so we just need to call that and then yield the items, like so: def iterateTree(tree): def callback(data): yield data tree.walk(callback) Then you can just use it like so: for x in iterateTree(tree): print(x) Perfect, right? Actually, no. This doesn't work at all. You can't yield from the callback passed to walk. That's because walk() itself doesn't know that the callback is a generator. This is the problem with generators: they divide all functions into two categories: regular functions and generators. You run a regular function by calling it. You run a generator by iterating over it. The caller must use it in the correct way. At its simplest level it means you have to be careful when refactoring. If you have a generator function that gets too big and you want to split it up, you have to remember that the functions you split out are also special generator functions if they contain a yield. You have to remember to flatten it when you "invoke it". It's more than just annoying though: it means it's impossible to write code that works generically with both kinds of functions. In other words, all of your higher-order functions like map, filter, etc. now only work with some of your functions. (Or, I suppose, you could explicitly implement them to support both but that's more work and I don't think most languages do.) In languages like Lua, the above code just works. You can yield from anywhere in the callstack and the entire stack is suspended. It's fantastic. (If I can be forgiven a bit of self-promotion, I'll note that my programming language Wren[1] can not only express full coroutines like Lua, but also supports symmetric coroutines which can express some things Lua cannot. They are roughly like the equivalent of tail call elimination for coroutines.) [1]: https://github.com/munificent/wren https://github.com/munificent/wren
- kansface 13y agoPy3000 added support for the "pass a generator" construct that you care about: http://simeonvisser.com/posts/python-3-using-yield-from-in-generators-part-1.html http://simeonvisser.com/posts/python-3-using-yield-from-in-g....
- munificent 13y agoAs far as I can tell, that's still just a little local syntactic sugar for making composing generators nicer. There's a still a fundamental distinction between generators and other functions. Think of it like this. Let's say you have a chunk of code that you want to refactor out into a separate method. The usual way to do that is to pull it out into a separate method and then call it from the place where the code used to be inline. In languages with coroutines, you can just do that, regardless of what's in that chunk of code. In Python, you have to think, "Oh, does this chunk of code contain a yield?" If so, you need to do a "yield from" the function you pulled out instead of a regular call. It forces you to constantly be cognizant of and design around the split between normal code and generators.
- crucialfelix 13y agoGreat write up - make that a rough draft for your post. I was going to edit my comment earlier to point out that coroutine is not generator. I realized this limitation one day while trying to do it in python. You cannot just yield another stream. SuperCollider has proper co-routines: http://danielnouri.org/docs/SuperColliderHelp/Core/Kernel/Routine.html http://danielnouri.org/docs/SuperColliderHelp/Core/Kernel/Ro... and the pattern library is built entirely around embedding in streams and yielding others streams. it uses this for very interesting numeric music patterns. Python 3.4 also now has co-routines: https://docs.python.org/3.4/library/asyncio-task.html https://docs.python.org/3.4/library/asyncio-task.html esp this is interesting: result = yield from future – suspends the coroutine until the future is done, then returns the future’s result, or raises an exception, which will be propagated. (If the future is cancelled, it will raise a CancelledError exception.) Note that tasks are futures, and everything said about futures also applies to tasks. result = yield from coroutine – wait for another coroutine to produce a result (or raise an exception, which will be propagated). The coroutine expression must be a call to another coroutine. Javascript does/will have simple generators
- VikingCoder 13y agoI've been using threads to do co-operative multi-tasking, for a while now. Every place that I'm tempted to write an event-driven finite state machine, or something similar, I spawn a thread instead. I get to write synchronous code, which feels much more natural to me. For instance my actor, running in a thread, calls a function like advance(). That drops data into an object, and wakes up the main thread, and blocks. The next time the main thread wants to give processing time to the actor, it describes the world into the same shared object, waked up the actor's thread, and blocks. Doing a switch like this dozens or even hundreds of times per second seems to work pretty well, especially if the main thread only gives execution to the actor thread when it needs to - inputs have changed, etc. For my use cases, it radically simplifies my code, and I have a small number of different inputs to handle, so it has been scaling well.
- srean 13y agoThat is precisely the beauty of coroutines: your code can look like threaded code but you can get the performance of an event-loop. Put another way, with coroutines you get most of the advantages of an event loop but very few of its disadvantages. (You get concurrency only, not parallelism, well this is almost true). For cooperative multi-tasking, threads are unnecessarily resource hungry and wasteful. They hoard resources although most of the time they are doing nothing just waiting. This is usually fine if you think yours is the only application that should be running on the hardware at that time, but typically you want to share the hardware with others. Remember not everybody has the luxury of working on a system where you can spawn 10,000 threads without breaking a sweat. However this territory of literally hundreds of thousands of programmed agents participating in a game does not seem to be very populated. Perhaps part of the reason is that very few languages had efficient (this eliminates stack copying), scalable and portable support of coroutines. This is starting to change, but not as fast as I would like. I think C is to be blamed for the long under appreciated status of coroutines. It is one abstraction that C left out, although the VM C had as its execution model (the PDP) had excellent support for coroutines at the instruction level. C exported pretty much every abstraction of the underlying instruction set, but not coroutines. EDIT: @VikingCoder Replying here as HN wouldnt allow me to respond till some time has past. Yes I have looked at asio although just scratched the surface. It looks very interesting, as far as I know they are not threads though (which is a good thing), they use macro and template metaprogramming trickery to turn producer-consumers into one big switch case. If you interested in coroutines and seamless interaction with C++ I can recommend http://felix-lang.org http://felix-lang.org