Nasal for C++ programmers

From FlightGear wiki
Jump to navigation Jump to search


Some History on Scripting in FlightGear

Cquote1.png Actually It didn't start with Nasal. I first implemented JavaScript (ECMAScript) for FlightGear but everybody thought the library was too large so it never was committed. Then Steve Baker put his PSL scripting language in plib and David (Megginson) added the first implementation of that to FlightGear. At that time we were (theoretically) able to run both JavaScript and PSL scripts from within one single XML file[1]
— Erik Hofman
Cquote2.png
Cquote1.png I'd caution against a special-purpose language; these things are almost always just as hard to write as real languages are, and never quite do as much as you hoped. I'd stick with a general purpose language, whatever you do. And now the plug. :) I wrote a scripting language of my own at one point (http://www.plausible.org/nasal) which is closer to Perl or Javascript. It's semantically richer than PSL, supporting all the stuff you expect like vectors, hashes/objects, garbage collection, et. al. It's also quite small; a little over 100k of source code these days. No work has been done to integrate it into FlightGear/SimGear (I wrote it for my own game project last spring), but I'd be happy to do so if there was interest. Languages are like religions though. Some people are going to hate the idea, some people are going to like it except for one or two things that *have* to be fixed first, some will want to use a different language. No one seems to want to use PSL yet, though, so it seems to me like the door is still open for alternatives. :)[2]
— Andy Ross
Cquote2.png
Cquote1.png Some will remember a few months back when the subject of scripting languages came up that I mentioned that someday I'd love to write my own language. Existing embeddable languages are either too limited or too large, and that I though there was a sweet spot for a small, simple language that still managed to support the "standard" list of features programmers have come to expect.[3]
— Andy Ross
Cquote2.png
Cquote1.png It was later that Nasal replaces psl because it was already much more integrated with FlightGear (thanks to Andy).[4]
— Erik Hofman
Cquote2.png
Cquote1.png All the FG specific stuff (APIs, extension functions, libraries/bindings) would almost definitely look exactly the same no matter the programming language being used, absent some major cleanup work to re-organize the FlightGear main loop (e.g. using threading and/or HLA).

Then again, the main issue with Nasal is not what it looks like (syntax) or how it works internally (codegen/VM), and not even it being de-facto unmaintained, but the way it is integrated into the FG main loop and the fact that there are structural wrappers missing (think SGSubsystem and friends) to move away from completely unstructured "free-form" code that is merely using timers and listeners to implement complex subsystems in scripting space without having any way to formalize data/code dependencies - as well as the fact that existing FG/SG code isn't exposed to scripting space, which is why there's so much redundant (and inefficient) code being re-written from scratch -often by non-coders.


— Hooray (Thu Apr 02). Re: Cloning fgdata with GIT submodules.
(powered by Instant-Cquotes)
Cquote2.png
Cquote1.png Even if Nasal were to remain unmaintained for another decade, it could very well continue to thrive in FG (in terms of adoption rate and documentation/examples), as long as those issues are addressed, i.e. better interfacing with/exposure of existing SG/FG code, and more structural wrappers to actually implement subsystems using OOP (classes) instead of just timers and listeners. Fool-proof multi-threading support would another useful thing though.
— Hooray (Thu Apr 02). Re: Cloning fgdata with GIT submodules.
(powered by Instant-Cquotes)
Cquote2.png
  1. Erik Hofman (Fri, 26 Mar 2004 01:24:44 -0800). Re: [Flightgear-devel] Trademark violations could be a problem.
  2. Andy Ross (Thu, 13 Nov 2003 08:54:46 -0800). [Flightgear-devel] ACScript RFC (or FGScript ??).
  3. Andy Ross (Mon, 17 Feb 2003 15:30:41 -0800). [Flightgear-devel] Nasl (not another scripting language).
  4. Erik Hofman (Fri, 26 Mar 2004 01:24:44 -0800). Re: [Flightgear-devel] Trademark violations could be a problem.

Nasal Scripting

If you are familiar with web browsers and JavaScript, then FlightGear scripting should be pretty straightforward:

Basically, Nasal scripting works exactly like JavaScript in your browser: Nasal is an embedded extension language, that is integrated into FlightGear as a conventional FlightGear subsystem (a so called "SGSubsystem") - during each frame, all subsystems, including the Nasal interpreter gets its time slice to process scripts - which is why badly written scripts may slow down the whole sim and affect frame rate/frame spacing.

Once FlightGear is up and running, most Nasal scripts will be invoked via timers and/or listeners. Other scripts can be embedded in certain XML files, or even typed and executed via the built-in Nasal Console dialog or the new Canvas driven Interactive Nasal Console.

Just imagine a bunch of FlightGear subsystems like these, which are all run sequentially:

  • weather
  • FDM
  • I/O (Joystick, Pedals, Yoke)
  • GUI
  • Nasal Scripting Interpreter
  • timers (events)
  • listeners (property tree)

The way that Nasal is integrated it doesn't really touch the core of FlightGear much at all; they just hook a new subsystem into the global list with events (timers) and the property tree now having support for invoking Nasal callbacks. SimGear is affected only in that it gets a new directory and a new library to build.

There is some documentation on the language available at:

Cquote1.png The files in the Nasal directory of the base package are read and executed at the end of boot. Stuff in fg-command bindings is parsed and executed when the binding fires. Properties under /nasal are read during initialization and can contain either literal code or point to files to load. And there are C++ APIs from which you can turn a string into a "FGNasalScript" object with a call() method which can be invoked at runtime.[1]
— Andy Ross
Cquote2.png


Cquote1.png The FGNasalSys subsystem now has a parseScript() method that returns a pointer to a "FGNasalScript" object. You can use this to hold onto a compiled script across multiple invocations and avoid the extra parsing/code generation overhead. [2]
— Andy Ross
Cquote2.png
  1. Andy Ross (Tue, 16 Nov 2004 17:43:10 -0800). Re: [Flightgear-devel] nasal?.
  2. Andy Ross (Thu, 04 Dec 2003 19:07:54 -0800). [Flightgear-devel] New stuff.

To learn more about the integration side of things, please see http://www.plausible.org/nasal/flightgear.html


Cquote1.png Nasal has always been a fully functional language, with recursion, lexical closures and anonymous lambda expressions. :) This feature allows for calling Nasal scripts (not functions) recursively. That is, have a command binding that is a Nasal script, that then calls another FGCommand object that is implemented in Nasal. Before, this wasn't possible because there was only one execution context for the scripting engine. Now you can have lots. So you can, for example, stash one away on a timer while another one runs. This allows for pseudo-multithreading of many scripts at once, which run forever in loops like: while(1) { if(NeedToUpdate()) { DoSomethingUseful(); } sleep(0.2); } This kind of programming can be simpler than running from a timer. Although the context is fairly heavy-weight compared with a timer entry, so we don't want thousands of them sitting around.[1]
— Andy Ross
Cquote2.png
  1. Andy Ross (Tue, 07 Dec 2004 10:36:23 -0800). Re: [Flightgear-devel] New nasal features coming.

Nasal itself has a standard library of general purpose functions, in addition it supports dozens of FG-specific extension functions to do various FG-related things, for example to interact with other subsystems (e.g. AI traffic, weather, FDM, GUI etc). In other words, while you'll find lots of standard library functions, there are many FG/simulation-specific functions available in FlightGear/Nasal.

An increasing number of subsystems in FlightGear no longer have direct scripting bindings via extension functions, but instead work by replicating their APIs on top of the property tree - so that these systems can be mostly controlled just by setting/getting properties - the autopilot system, AI Traffic and the canvas are some more recent examples of this. Unlike conventional subsystems, C++ code or Nasal code, such systems can easily be controlled via the property tree - so that you can even control these systems via Telnet. To add new bindings to Nasal, the cppbind framework in SimGear/Nasal should be used (the Canvas system and NasalPositioned being the implementation reference here for the time being).

One of the oldest mechanism to run custom code are so called "fgcommands", which is just a fancy word for "FlightGear commands" that work purely via the property tree - i.e. they accept parameters solely as properties, and they may also only return values via the property tree. So these know nothing about Nasal or Nasal's data structures - which makes them pretty simple and still flexible, because they can also be used in non-scripting contexts, such as in XML files/bindings - from Nasal space, these can still be easily invoked via a dedicated fgcommand() API.

Also, there are extension functions available which allow callbacks to be registered in the form of property listeners (which get invoked whenever a property is updated/written to) or via timers (that may expire and trigger a callback that gets called after expiration). So you can basically make up your own control property /sim/foo/control-bar and register a listener callback that gets invoked whenever the property is updated (via cockpit hot spots, the GUI, autopilot, C++ code other Nasal code).

You will typically want to use listeners to get notifications whenever a certain property changes - a good example is a cockpit button or a GUI control that's updated.

Timers are usually used to regularly run a piece of code, i.e. to do some form of background processing - stuff that doesn't need to be running all the same, but that needs to be run at regular intervals (such as 1hz, 5hz, 10hz). Often times, callbacks invoked via timers will typically set up a new timer at the end of the routine, so that they basically invoke themselves. A more recent (and more sophisticated) option is the new "maketimer()" API.

Finally, there's a plethora of FlightGear-specific modules in $FG_ROOT/Nasal that greatly simplify coding stuff from scratch.

There are two aspects to Nasal: that of interaction with itself and with the property tree. For instance, to control an aircraft in a scripted fashion, the property tree is going to be your communication with the wider FlightGear code. See the following video of a fully scripted f14b carrier approach, using just Nasal scripting and the property tree:

In such a scenario, you will mainly use the autopilot to control the aircraft (since it has a real FDM it's kinda hard to control), and we would definitely recommend one with a well-developed autopilot (the 777s are really excellent with everything and has a good autopilot with all features you want). One option with the 777 is that you could potentially use Nasal to dynamically edit routes that the aircraft just flies via the route manager – not much work there. Another approach would be manually adjusting the autopilot parameters (like heading hold, bank angle, and stuff like that) through Nasal feedback algorithms for aircraft that don't have a.route manager. You could also couple this with the XML autopilot (now called "property rule") system to have real PIDs, easy lowpass, etc.

Nasal also can use its own mechanisms (library functions/APIs) to interact with different things (file system, route manager, fgcommands, ...). In interest of the web browser analogy, XML in FlightGear is like HTML.

Specifically it is responsible for setting up much of the property tree that FlightGear's subsystems act off of. David Megginson (creator of FG's property tree) once said that "XML should provide the nouns, Nasal should be the verbs" (or something, that isn't a direct quote)

Cquote1.png Basically my rule is that markup languages are nouns and adjectives, while programming languages are verbs and adverbs. You use a markup language to describe information and programming languages to act on it.[1]
— David Megginson
Cquote2.png


Cquote1.png Originally, I defined actions using XML mainly because we didn't have a scripting language, but ideally, we should share up the labour like this: XML = declarative NASAL = imperative i.e. if we want to say "this is X," we use XML; if we want to say "do Y", we use NASAL. A drop-down menu is declarative, but the actions associated with each menu item are imperative, and could be NASAL code snippits. The keymap is declarative, but the actions associated with each keystroke are imperative, and again, could be NASAL code snippits embedded in XML.[2]
— David Megginson
Cquote2.png
Cquote1.png Completely agree. Although it should be pointed out that there's significant gray area between the two extremes. Some of the existing commands (like property-adjust, with its handy wrap and clamp options) are simpler than the equivalent Nasal code for typical usages. Likewise, sometimes declaring your data in Nasal can save a lot of work interpreting that data on the C++ side.[3]
— Andy Ross
Cquote2.png
  1. David Megginson (Tue, 09 Jul 2002 11:07:39 -0700). Re: [Flightgear-devel] x++ The World's First XML-Based ProgrammingLanguage.
  2. David Megginson (Wed, 26 Nov 2003 15:12:46 -0800). Time to Refactor (was Re: [Flightgear-devel] Re: Nasal integration docs).
  3. Andy Ross (Wed, 26 Nov 2003 16:35:23 -0800). Re: Time to Refactor (was Re: [Flightgear-devel] Re: Nasal integration docs).

Historically, FlightGear fgcommands were used before scripting got introduced, which is why there's some overlap between scripting and the legacy fgcommands, meanwhile we've come "full circle" - so that fgcommands can now be implemented in Nasal, and even overridden.

So PropertyList XML is really good at providing data that Nasal can work off of. For like the fox2.nas script, this means that ideally all of the specific data about the missile would be taken from Nasal and stored in an XML file (which would be loaded by the Nasal file and the data would be copied over).

Some words on Nasal for fellow C++ programmers

Compared to C++, there is really nothing "low quality" about Nasal per se: Nasal is just the "script glue" that connects different parts of the simulator: Many Nasal scripts leverage C++ code - and it is very easy to add new C++ code that can be called from Nasal. So this is a perfect option to combine the speed of C++ with the flexibility of scripting, see Nasal/CppBind for details.

History has shown, that most code in FlightGear will eventually be made more configurable and more accessible, this usually happens in the same steps:

  • replacing static variables with variables stored in the property tree
  • using listeners and callbacks to get update notifications for important variables
  • adding new fgcommands
  • fully exposing a "control" interface by making it accessible in the property tree (see e.g. the AI/autopilot or canvas systems)
  • providing new scripting hooks using extension functions (see Howto:Extend Nasal)
  • providing a full OOP interface (using simgear/nasal/cppbind) (see $FG_SRC/Scripting/NasalPositioned.?xx)

Even if you should know C or C++ already, Nasal probably remains the most accessible and the most powerful method for customizing the simulator in the beginning, simply because it is extremely easy and fast to get started, you don't need an "integrated development environment", you don't need to install compilers and you don't need to satisfy any 3rd party dependencies; bottom line being: if you can run FlightGear, you can also run Nasal and directly create new features.

In addition, Nasal code is fairly abstract code, too. Once you start looking at some existing Nasal scripts, you will see that it is also fairly high level code, much more high level than C++ or C- so Nasal has a much higher density of code, too. Nasal's role in FlightGear really is like JavaScript's role in Firefox or Chrome, where it is also used for many/most core-related logics (CSS/XUL).

Nasal is an extension language, a scripting language that is much more high level than C++ and specifically meant to be used for extending a host application (FlightGear). C++ on the other hand is a systems programming language (most APIs in Nasal are FlightGear specific).

Nasal's power is due to the fact that it is not as low level as C++ code, and that it is very easy for non-programmers to get started adding custom features to FlightGear, without requiring a background in software development or programming.

Aircraft developers or other base package contributors don't need to be familiar with C++ programming, they don't even need an IDE or a working build environment. They can just write scripts in a text editor and drop them into the base package, and things will simply work. That is because FlightGear is the runtime environment for Nasal scripts.

Obviously, an experienced programmer can make use of more complex Nasal features, and come up with much more optimized code and sophisticated features.

Basically, there really is nothing in either language that you cannot also do in the other language - ever heard of "turing completeness"?

Of course, some things are better handled in C++ space, while others are better (=more easily) done in Nasal, usually you'll want to implement performance-critical stuff in C/C++ and then make it accessible to scripting space.

It really boils down to what needs to be done, usually new features can be implemented (or at least prototyped) with just some Nasal code, sometimes new Nasal functions may be required to interface to the rest of FlightGear.

Some words on plugins

Experienced C++ developers may be looking for an SDK to create plugins for FlightGear. This is a discussion that has been brought up frequently by new FlightGear users who would like to create new features/modules for FlightGear.

FlightGear doesn't have such an SDK, and the general consensus is that FlightGear doesn't need an SDK for creating binary plugins. FlightGear is an open source flight simulator, developers are invited to directly contribute to FlightGear itself.

While adding support for binary plugins is of course possible, and not even that complicated, it would be a pretty bad idea for a number of reasons:

FlightGear supports a fairly powerful (and high level) scripting language to provide extensibility - dynamically linking to binary plugins (DSOs, DLLs) however is highly platform specific and has also many other disadvantages (e.g. mutual library dependencies and versioning requirements).

Not to mention, that plugin developers need to be able to build/compile their modules before using them. Nasal scripts on the other hand "just work" by dropping them into the base package folder, in its current form, there's nothing platform-specific about Nasal code - if it runs on one machine, it will also run another - regardless of your CPU, GPU or operating system.

Instead, FlightGear is usually extended by either using its support for Nasal scripting or by adding new C++ code to the source tree, or a combination of both approaches (so that Nasal scripts can call C++ code).

Occasionally, plugin developers for commercial flight simulators such as MSFS or X-Plane highlight how important such a plugin system is. But the truth is, it is only since fairly recently, that there are discussions going on in the X-Plane community to also add scripting support to their simulator, ironically by the use of a python plugin specifically for scripting:

Cquote1.png What we need is a scripting system. The scripting system would provide a relatively simple text-file syntax to do simple scripting of systems and instruments for airplanes.
— X-Plane Scenery Blog[1]
Cquote2.png

Now let's look at this decision: They have like how many years experience working with binary plugins? How come they still intend to support scripting, probably because it's the better solution!

So, in this aspect, FlightGear is really years ahead of the other two large flight simulators, both of which provide fairly limited script-ability.

Also, you cannot circumvent the requirements of the GPL 2, so any binary modules (plugins) linked into GPL'ed software, need to be GPL'ed too - which requires the corresponding source code to be also distributed alongside with any binary releases.

If you want to add new features to FlightGear, the usual way is to add a new subsystem - for an example of how this is done in C++, you may want to search the FG forums for the discussion on "ridge lift" that took place a while ago which shows how the fellow forum member "WooT" implemented support for ridge lift by adding a new subsystem to FlightGear, see these threads for details:

Related discussions:

Performance

Obviously, C++ code will usually be faster than the corresponding Nasal code. But, while performance is not a design goal, Nasal isn't especially slow either. For example, early benchmarks of the garbage collector showed it as faster than perl's reference counter, and its number crunching performance is on par with python. But in all cases, simplicity, transparency and a sane feature set are preferred over speed in Nasal.

Nasal was specifically designed for use as an extension language in an larger project such as FlightGear. The problem with many otherwise excellent languages in this environment is that they are huge. Perl and python are great, but enormous. Even their "core" interpreters and library code are larger than most projects that require an embedded language. They cannot be readily shipped with their host application and need to be installed system-wide. This is a pain and a compatibility hassle.

The real goal with Nasal is to have a language that supports most "normal" programming idioms (objects, functions, arrays, hashes) while avoiding the bloat that comes from "platform" scripting languages like perl, python, ruby and php.


Garbage collection

Nasal garbage collects runtime storage, so the programmer need not worry about manual allocation, or even circular references. The current implementation is a simple mark/sweep collector, which should be acceptable for most applications. Future enhancements will include a "return early" capability for latency-critical applications. The collector can be instructed to return after a certain maximum delay, and be restarted later.


As far as speed goes, the last time any benchmarking for Nasal was done, it was about as fast as Perl 5 or Python 2.2 at most things. It's garbage collector was faster, its symbol lookup about the same or slightly faster, and its bytecode interpreter somewhat slower.

Meanwhile, the GC has been identified as a potential bottleneck in FlightGear, see How the Nasal GC works for details.

Thread safety

Unlike almost all other script interpreters (and unlike the FlightGear/Nasal interface itself currently) , Nasal is thread-safe and scalable when called from multiple CPU threads (as opposed to the user space interpreter threads implemented by Ruby or the GIL used by Python for example).

No special treatment is required (as for perl, which clones a separate interpreter with separate data for each thread and uses locking around specifically-designated shared data) and the threads can be scheduled simultaneously. There is no global lock on the interpreter, as used by Python or Lua. The only limit on scalability is garbage collection, which must block all interpreter threads before running.

When running threaded code, Nasal provides "minimal thread safety", meaning that the interpreter itself can be safely called from multiple CPU threads without risk of corrupting or deadlocking the interpreter internals. Multithreaded operations are therefore "safe", although they are not guaranteed to be atomic. In particular, poorly synchronized insertions into containers can "drop" objects into oblivion (which is OK from an interpreter stability standpoint, since the GC will clean them up normally). Nasal itself provides no synchronization primitives to address this; thread architecture is a "top-level" design job, and Nasal is intended to be an extension language in a larger project. Choice of synchronization mechanisms is going to be highly application dependent.

Exception handling

Like python, nasal supports exception handling as a first-class language feature, with built-in runtime-inspectable stack trace. Rather like perl, however, there is no special "try" syntax for exception handling, nor inheritance-based catching semantics. Instead, you use the call() builtin to invoke a function object and inspect the results to determine what error was thrown (either with the die() builtin or via an internal runtime error) and what the stack trace looked like. Elaborate exception handling isn't really appropriate for embedded scripting languages.

High level programming

Thus, programmers already familiar with C++ shouldn't just disregard Nasal as a "toy" that doesn't seem suitable for serious development: some of the more complex Nasal scripts can literally make one's head spin around and it would quite obviously take much more C++ or Java code to implement the same features, while sacrificing all the flexibility and power that a scripting language offers.

Some features can certainly be more easily implemented in Nasal space, than in C++ space. Often, the Nasal solution is "on par" with similar solutions in C++.

Accessibility

For instance, Nasal code cannot only be easily run and contributed by all users, but it can also be easily reused and maintained by other users. This means, that given the number of active C++ developers, compared to the number of base package contributors, your code is more likely to be actively maintained by fellow users if it is written in Nasal.

In other words, if there are some experimental features you'd like to explore, Nasal is an excellent way to ensure that other FlightGear users can easily test your new features. This could be witnessed during the development of the local weather system or the bombable addon,too.

This is in stark contrast to features developed solely in C++ space, because these can usually only be tested by people able to build FlightGear from source, especially if your code isn't yet in the main repository, where it would eventually be available in the form of a binary snapshot.

Obviously, none of this is to say that Nasal is the perfect solution for any problem, there are many things for which Nasal isn't necessarily a perfect choice, such as low level code for example (i.e. rendering).

On the other hand, Nasal really is a powerful tool in FlightGear, and if you find that something should, but currently cannot, be done in Nasal space, it is extremely easy to add support for new features to the Nasal engine using extension functions or property listeners to trigger C/C++ code.

Shouldn't we favor C++ over Nasal?

Cquote1.png if you have a task that consumes 0.01% of the total computational performance, it doesn't matter whether you do it efficient [C++/GLSL] or inefficient [Nasal] - if you make it ten times faster, the framerate won't move a bit, and if you make it a hundred times slower, the framerate won't move either. Given that, you can priorize accessibility and ease of maintenance over execution speed.

(To give you a sense of perspective - the typical Nasal subsystem runs a couple of hundred lines per frame. Rendering at high quality runs about 1500 lines for each of your two million pixels every frame and loops over an array containing a good million vertices). That's about a factor of thirty million more operations per unit time. So FG doesn't lean 'heavy' on Nasal in terms of where the work is.


— Thorsten (Sat Feb 14). Re: Sometime, with Yasim... frame-rate is slow.
(powered by Instant-Cquotes)
Cquote2.png
Cquote1.png you can of course misuse Nasal to do heavy computing duty, in which case it eventually will slow down FG (if you inspect Nasal arrays with millions of entries every frame, it won't be a tiny amount of computation any more). But as long as you use Nasal for things that are not heavy-duty work (i.e. involving loops over thousands of entries, running code triggered by hundreds of listeners at the FDM rate...) the above argument holds.


The bottomline is - you need to optimize framerate by optimizing the part of the code where most performance is burned. Targeting subsystems with insignificant performance is pointless for optimization (still nice conceptually though...).


— Thorsten (Sat Feb 14). Re: Sometime, with Yasim... frame-rate is slow.
(powered by Instant-Cquotes)
Cquote2.png

Performance-critical code should definitely be implemented in C++ space. Still, there are several options, such as dedicated C++ subsystems (SGSubsystem) or Nasal extension functions.

Please, if you do know C++ and the SG/FG code bases, you are obviously encouraged to contribute directly to the core source code - nobody is trying to keep people from contributing there - see Howto:Start core development.

That said, among some core developers, there's apparently the misconception that other FlightGear contributors increasingly favor Nasal over C++.

Obviously, some core developers are particularly concerned about the amount of Nasal code added to FlightGear (i.e. the base package) recently.

So to put things a little into perspective, and trying to explain the current situation:

  • We must not miss the fact that these are complaints about the plethora of Nasal code added to FG by non-core developers, these are usually people who don't know C++, who don't know the FG/SG code bases and who don't know how to build FG from source or at least who don't regularly rebuild a custom/patched binary from source, and who don't have commit privileges to directly contribute to FlightGear.
  • It's not that C++/core developers suddenly decided to stop writing C++ code and instead use Nasal, Rather, the opposite is true, there are still a number of core developers who refuse to use Nasal at all and stated so publicly.
  • So let's face it: How many "qualified" Nasal coders can you name? How many Nasal developer are even able to program in C++, are familiar with the STL and Boost? How many Nasal coders would be potential C++ core developers? And then, how many of them are able to use git and gitorious? How many are actually able to build FG from source?
  • For example, up to a certain point, the local weather system has been entirely developed without ever building FG from source, and without using git.
  • While writing Nasal code is obviously much simpler than writing C++ code, writing really good Nasal code is still difficult - on the other hand, writing really bad code in Nasal is much more difficult than in C++.
  • Just because someone is able to write a script in a dynamic, untyped, garbage-collected programming language doesn't automatically mean that they are able to write quality C++ code that can be added to the main code base, using dependencies like the STL, Boost, OpenGL, OSG etc.
  • The truth is, there's TONS of Nasal code in the base package that has an impact on performance, because of the way it is written, not primarily because of the well-known GC issues.
  • Often, most high quality examples of Nasal code were written by people who also happened to be core developers.
  • So it's way too simple to say that forum users are writing Nasal because they feel it's "just better". And, clearly, nobody on the forum wants to replace fgfs.exe with "nasal.exe"[1]. It's really just polemics to suggest that forum users feel "core=bad" and "nasal=good", forum users tend to contribute in the form of Nasal code, because the barrier to entry is so much lower, and because their work can be deployed much more easily.
  • For forum users, it all boils down to an accessibility issue: Nasal is obviously much more accessible than C++ coding, not just for the developer of a feature, but also for people wanting to use the feature.
  • Yes, it's true, Nasal code makes the main loop non-deterministic, i.e. because of the garbage collector - but there are people currently working on improving the GC: How the Nasal GC works.
  • On the other hand, we also have tons of C++ code which is not using smartpointers and yes, which is leaking memory - in other words which is "non deterministic", too (definitely from an embedded developer's perspective).
  • Now "fixing" non-deterministic Nasal code involves re-implementing a single self-contained component (a single C file actually, with ~320 lines of code), the Nasal GC - or even just adding support for an existing GC implementation. Which is something that is currently being worked on by another contributor, so that the GC impact will eventually be not as severe probably.
  • Making the rest of FG stop leaking memory involves much more work unfortunately.
  • And then, we have threading-related segfaults because of C++ code, where running the same binary with the same arguments sometimes works, and sometimes simply doesn't, and crashes FG.
  • Really, writing badly performing C++ code is much easier than writing bad Nasal code. There are only a handful of ways to crash FG from Nasal.
  • And writing/reviewing Nasal code doesn't involve the same skills that C++ programming requires.
  • Nasal code doesn't need to be reviewed and committed by core developers - it can be peer-reviewed by other Nasal programmers, which is often happening on the forums.
  • On the other hand, we've seen quality C++ code submissions which ended up not being committed or even reviewed for months (or even years)
  • Deploying contributions written in Nasal is MUCH simpler, and doesn't necessarily involve any core developers!
  • When compared to Nasal, we have less people in the FG communtiy who know how to program C++ and who know how to build FG from source, that's what it all boils down to, really.
  • We cannot possibly replace or rewrite the sheer amount of scripted code we have, and looking at all the features implemented in Nasal, most of them cannot as easily, or simply shouldn't be implemented in C++ at all. After all, FG is all about being open and extendable for end-users.
  • It's true, we get to see an increasing amount of features being developed in the base package. Which is a good thing because not every conceivable feature can be developed by core developers in C++ space. Many of these features were developed in Nasal because it was possible and simple.
  • Contributing to the C++ code, simply isn't easy. Which is not meant to sound negative: it's a long-standing issue. Also, anything coded in C++ is obviously not as flexible and configurable, not without making excessive use of properties and XML config files, and then there are still ugly corner cases like tied properties or properties only read once during startup, or tags that only support static value and no properties at all.
  • The other problem is that there is simply a shortage of active C++ developers familiar with the project and able to build from source.
  • So, it is MUCH easier to develop MOST end users features in scripting space than in C++. That's one part of the reason why we sometimes get to see tons of new features showing up in the base package, while only very few new features show up in the SG/FG repositories in the same month.
  • Anybody complaining about the degree of Nasal additions to FlightGear, needs to keep in mind that even developing just a single feature in C++ space takes often much longer (and involves more people, if they don't happen to be core developers), this applies especially to inexperienced programmers.
  • So whenever somebody is complaining about the plethora of scripted code in Nasal, it is a good idea to keep the user (non core developer) perspective in mind: people are trying to bring changes to FlightGear, to contribute in a meaningful way, without blocking each other and without facing a steep learning curve, without being limited by the number of releases per year or by core developers having some time to mentor them.
  • Nasal provides a very real chance for people to innovate and develop features without depending on core developers and without having to be able to build FG from source
  • We have tons of features that we would not have without Nasal in the first place, including the local weather package and the bombable addon - these are features that probably would have never been developed by core developers. Frankly, because C++ is just too low level a language for such things and because features implemented in C++ space often end up there because someone cared enough to implement them in the first place, not because some end user "requested" them.
  • if the primary concern is really shifting the development focus to increased use of C++ instead of Nasal, then, it needs to be made much easier for new developers to get involved, which includes reviewing patches and merge requests. At the moment, the project simply isn't there yet - which mostly boils down to a lack manpower obviously.
  • Also, what's the alternative here? That people stop developing their own features just because we don't have enough active core developers who can provide help and support to newcomers and actually mentor them to a degree such that their patches can be reviewed and committed?
  • This is not a problem about Nasal at all, it's a manpower and accessibility problem: due to FG's flexible design we have more and more users who want to add to it and extend/improve FlightGear. This however isn't particularly straightforward for people without solid multiplatform, C++/OOP, programming experience - they have to face an extremely steep learning curve. Nasal simply provides the lowest entry barrier here.


Seriously, people who tend to write code in Nasal rather than in C++, usually do so for a reason - with Nasal we have:

  • pretty good documentation
  • a fair number of beginner tutorials
  • lots of examples and snippets
  • people able and willing to help
  • dedicated sub forum
  • experienced Nasal coders mentoring new contributors
  • a fully working development environment, i.e. no need to set up a separate build environment
  • no tedious merge requests necessary, or long code reviews
  • no need to wait for a new release to use and distribute a new feature (remember, just 2 releases per year)

In other words, if you are a C++ core developer who wants to see more C++ code added to FG instead of Nasal code, try to beat that! ;-)

So a user might argue that, more or less active core/C++ developers are complaining about features getting implemented in scripting space without volunteering to implement them in C++ space instead.

Given the shortage of active C++ developers, and an increasing number of people being able to write Nasal code (and develop moderately complex features in scripting space) it isn't really surprising that we are seeing so many features being implemented in Nasal currently.

Really, what we've been seeing is a simple form of evolution where the project dynamics adapted to the situation at hand, by recognizing a lack of core developers and shifting the focus to where the manpower is, which clearly is not the C++ source code unfortunately ...

  1. Benjamin Supnik (14 February 2009). Scripting: A Line In the Sand.