Est. 2001 Intermediate

Ferite

A small, threadsafe, BSD-licensed scripting engine written in C, born out of one developer's frustration at embedding Perl, and remembered for its blocks-passed-to-functions syntax and its monitor/handle error handling.

Created by Chris Ross

Paradigm Multi-paradigm: object-oriented, imperative, procedural, with closures and block-passing
Typing Dynamic, declared types checked at runtime
First Appeared 2001
Latest Version Version 1.1.17 (22 October 2009); the GitHub development sources carry 1.1.19

Ferite is a small object-oriented scripting language and engine, written in C, whose reason for existing was never the language itself. It was written to be embedded: dropped into a C or Objective-C application so that the application could be scripted, configured and extended without shipping a second runtime the size of Perl or Python. The design goals its own manual lists are, in order, “lightweight - small memory and CPU footprint, fast, threadsafe, and straight forward” for both the host application’s programmer and the script writer.

It is a curly-brace language, and deliberately so. Its author described the influences precisely: Java for objects, C and PHP for functions, Scheme for closures, Ruby for block calling, and C++ for namespaces. What came out the other side reads like PHP that grew up around a proper object model, with a handful of ideas - monitor/handle/else error blocks, functions that accept a caller-supplied block via using, and deliver()/recipient() for passing those blocks onward - that are genuinely its own.

Ferite was substantially the work of one person, Chris Ross (who signed his commits and mail as boris or ctr), with a handful of regular contributors. It was never adopted widely enough to become a household name, but it did something most hobby languages never manage: it shipped in Debian and FreeBSD for years, and a real commercial product was built on top of it.

History & Origins

Started because embedding Perl was painful

The project’s own about page traces the beginning to Summer 2000, after around two years of the author playing with the idea, and is blunt about the trigger: frustration at trying to embed Perl inside an application. Anyone who has read perlembed will recognise the complaint. The ferite project was registered on SourceForge on 19 July 2000, under the categories code generators, compilers and interpreters, with C and Objective-C listed as its implementation languages.

The engine is a compiler and a bytecode virtual machine, both written in portable C, with an explicit C API for embedding and for writing native modules. Documentation copyright notices in the manual reach back to 1999, and the source licence header covers 1999 to 2005 - so the earliest code predates the public project by a year or so.

2001 to 2002: escaping into the distributions

Public releases were circulating by 2001: the FreeBSD ports tree gained lang/ferite on 14 October 2001, and an intent-to-package bug was filed against Debian on 9 January 2002 for version 0.99.4, naming Chris Ross as upstream and the licence as BSD. The description in that bug is the one that followed ferite around for years afterwards - “strong similiarities to perl, python, C, Java and pascal, while being both lightweight, modular, and embeddable”.

The 0.99.x numbering is worth noting, because it lasted a long time. The website’s front page carried the line “We are currently marching towards a 1.0 release - this has only taken use since Summer 2000 to get here so far”, and the stable version was reportedly still 0.99.6 as late as 2004. Ferite spent roughly four years in the run-up to 1.0, adding features that many languages would have shipped a major version for.

The credited team

The project’s team page names, besides Ross, Evan Webb (whose array implementation replaced the original), Stephan Engström (the Sys module and engine optimisation work), and Alex Holden (Sys module and fixes), with acknowledgements to Blake Watters for converting the documentation to SGML, Pete Vassof for the String module, and others for IPC work and general help.

Design Philosophy

Embeddable first, standalone second

Ferite’s pitch is that the engine is a library. The host application creates a script engine, exposes native functions and classes to it through the C API, and runs scripts; the ferite command-line interpreter is essentially a thin standalone user of that same API. The dependency list stayed correspondingly small - the FreeBSD port needed only libpcre and libxml2 - and threadsafety was a stated goal from early on, at a time when many scripting engines still had a global interpreter lock or worse.

The build also ships a documentation generator (feritedoc), a module scaffolding tool (generate-module), and farm, the project’s tooling for installing modules outside the engine’s own prefix.

Declared types, checked at runtime

Ferite variables are declared with a type - the manual lists number, string, array, object and void - but the checking happens when the assignment executes, not at compile time. The engine’s own test suite makes the behaviour explicit: assigning a string to a number raises a catchable runtime error, while assigning a string to a string does not.

number blarg = 10;

monitor {
    blarg = "blarg";
} handle {
    Console.println( "Failed: " + err.str + " (" + err.num + ")" );
} else {
    Console.println( "This branch does not run" );
}

That monitor/handle/else construct is ferite’s exception handling, and the else clause - a block that runs only when the monitored code raised nothing - is an unusual touch. Earlier versions spelled the same idea iferr/fix/else, and the iferr form survived into the 1.0 release notes, where a fix for iferr blocks inside loops is listed as one of the last changes before 1.0 final.

Key Features

Blocks passed to functions

The feature ferite is most worth remembering for arrived with the 1.0 release candidates: any function can be handed a block by the caller with the using keyword, and that block is a closure. It is the Ruby idea, expressed in curly-brace syntax:

uses "console", "array";

Array.each( [ 1, 2, 3 ] ) using ( value ) {
    Console.println( value * 5 );
};

Inside the called function, the block is reached through deliver(), which invokes it, and recipient(), which returns it as an object so it can be stored, invoked repeatedly, or forwarded to a third function:

function threeTimes() {
    deliver();
    deliver();
    deliver();
}

function passOnToThreeTimes() {
    threeTimes() using recipient();
}

threeTimes() using {
    Console.println( "Hello World" );
};

Standalone closures use the closure keyword and capture their enclosing scope, and are called through invoke():

function nTimes( void thing ) {
    return closure ( n ) {
        return thing * n;
    };
}

object o = nTimes( 23 );
Console.println( "${o.invoke(3)}" );

Strings interpolate with $name and ${expression}, in the Perl and PHP tradition.

Objects, namespaces, and modifying both

Classes support inheritance with extends, abstract and final modifiers, static members, static constructors, and access control - variables private and methods public by default, per the February 2003 announcement that introduced private, protected and public. From 1.0, constructors are named constructor rather than sharing the class name.

Namespaces come from C++ and nest, with a leading dot addressing the current namespace and super the enclosing one:

namespace blah
{
    number i;

    function blit( string blip ) {
        Console.println( "in blit: " + blip );
        blah.i = 10;
    }

    namespace inside
    {
        function insidei() {
            Console.println( "blah.inside.insidei()" );
        }

        function testi() {
            .insidei();
            super.blit( 'foo' );
        }
    }
}

The manual documents modifying existing classes and namespaces after the fact - reopening them to add members - which is closer to Ruby than to C++ or Java.

Regular expressions, moved out of the language

Early ferite had Perl-style regular expressions baked into the grammar, complete with backtick syntax. The 1.0 release notes record their removal from the language proper and their relocation into a module named regexp, on the grounds that a module was “more ferite esque”. The module keeps the block-passing style, so matching and replacing can both take a block:

uses "console", "regexp";

object o = new Regexp( "([0-9]+)" );

array a = o.matchAll( "123 456 789 345" ) using ( match ) {
    Console.println( "Got match: '${match.match()}' in range '${match.span()}'" );
};

string replaced = o.replaceAll( "1234 is the 123456" ) using ( match ) {
    return "${match.match()}.feriteRocks";
};

Other notable capabilities

  • Parameter overloading - several functions of the same name distinguished by declared parameter types, added in the January 2003 release.
  • Call by reference, written function f( number &b ), added in the 1.0 series.
  • Variable argument lists, reached through arguments() (renamed from getArgs() in 1.0).
  • Reflection, including runtime inspection of objects and functions and the ability to trace a variable’s modification.
  • Serialisation, with both a native and an XML form, plus a small pure-ferite remote method invocation framework.
  • Optional Boehm garbage collection - from 1.1.12 in February 2009, ferite would detect and use libgc by default.

Platform Support

The project’s about page lists the platforms it ran on as Linux, Solaris, Mac OS X, Cygwin and FreeBSD, and the SourceForge project entry records BSD, Linux, Mac and Solaris. Windows was a recurring aspiration rather than a supported target: the changelog contains entries about Windows fixes attempted ahead of the 1.0 release, and contemporary discussion noted the absence of a native Win32 port, with Cygwin as the available route.

Evolution

The version history divides cleanly into three phases.

PhaseRoughlyCharacter
0.99.x2001 - 2004Rapid feature growth under a pre-1.0 label: static members, namespaces extending namespaces, variable argument lists, include(), array initialisers, switch, an array subsystem rewrite, a new module system, foreach, ternary conditionals, access modifiers, and finally closures
1.0.x2005Consolidation. Rewritten manual, embedding guide and C API docs; the using block syntax and closure keyword; constructors renamed; regular expressions moved to a module; call by reference
1.1.x2006 - 2009Maintenance and performance. UTF-8 string fixes, Array.append, optional libgc, a compile cache, and a stream of bug fixes, ending at 1.1.17

Development effectively ended with the 1.1.17 tarball of 22 October 2009. The source was moved to GitHub in May 2010, with the module collection following the next month, and the last commits to the core engine were made in July 2011. The development tree there still carries the version number 1.1.19, a release that never shipped.

Current Relevance

Ferite is dormant, and has been for well over a decade. The clearest marker is FreeBSD’s removal of lang/ferite on 31 October 2024 with the note that it was “Abandonware and outdated, last release in 2009 (current version in 2005)” - the port had outlived upstream by fifteen years.

The one place ferite kept working for real money was Cention AB. Its GitHub organisation holds a maintained fork of the engine (ferite-1.1.18), an Apache module, a web framework, and modules written specifically for the Cention Suite’s email, export and reporting features - several with commits into the mid-to-late 2010s. Whatever else can be said about ferite’s reach, a commercial product ran on it years after its author had moved on.

Anyone wanting to run ferite today has to build it from source: the GitHub tree still uses autotools (./configure && make && make install), and there is no official or community Docker image. Expect friction from a C codebase last touched in 2011 meeting a modern toolchain - the FreeBSD port carried a stack of local patches for exactly this reason.

Why It Matters

Ferite is a good case study in a category of language that was crowded in the early 2000s and has almost entirely disappeared: the embeddable general-purpose scripting engine. Lua won that fight decisively, and Python and later JavaScript engines took much of the rest. Ferite made a defensible bet - a fuller object model and a richer standard library than Lua, a much smaller footprint than Perl - and lost anyway, largely for reasons that had nothing to do with the language: no corporate sponsor, one primary author, and a four-year march to 1.0 during which its competitors’ ecosystems compounded.

What is worth taking from it is the syntax. Ferite put Ruby-style blocks into a C-family language, gave them a first-class calling convention (using, deliver(), recipient()), and applied that convention consistently across its own libraries - iteration, regular expression matching, string replacement - years before blocks and lambdas became standard equipment in mainstream curly-brace languages. Its monitor/handle/else construct, with an else branch for the no-error case, shares a shape with Python’s older try/except/else - a shape most C-family languages still lack.

It is also a reminder of how much a small project can produce when someone keeps at it: a bytecode VM, a threadsafe embedding API, a documentation generator, a module scaffolding tool, book-length manuals, and a couple of dozen modules - all under a BSD licence, and all still readable on GitHub.

References

Timeline

2000
Chris Ross registers the ferite project on SourceForge on 19 July 2000. The project site later dates the start of serious development to Summer 2000, after roughly two years of experimenting with the idea, and describes the motivation as frustration with embedding Perl in applications
2001
Ferite reaches outside packagers: the FreeBSD ports tree gains lang/ferite on 14 October 2001, described as a scripting language designed to be embedded in other applications with low memory and CPU overhead
2002
An intent-to-package bug is filed against Debian on 9 January 2002 for ferite 0.99.4, listing Chris Ross as upstream author and the BSD licence, and describing the language as having strong similarities to Perl, Python, C, Java and Pascal while remaining lightweight, modular and embeddable
2003
A stable release announced on 8 January 2003 adds the foreach operator, ternary conditionals, parameter overloading, super() calls to a parent constructor, brace-free single-line functions, and a greatly expanded Sys module covering syslog, select(), UDP and Unix domain sockets, signal handlers, IPv6 and DNS lookups. The release notes claim speedups of between 3 and 12 times over the previous version, but publish no benchmark programs, baseline versions or hardware details
2003
A CVS update announced on 16 February 2003 introduces private, protected and public class members, abstract and final classes, and a backtrace printed when an error goes uncaught
2003
Work is reported in August 2003 on Ferite.framework, an Objective-C framework intended for dropping the engine into Cocoa applications on Mac OS X
2003
On 14 December 2003 Ross announces statically bound lambda constructs in CVS, drawing on the lambda calculus work of his PhD; the test script scripts/test/closure.fe dates from this effort
2005
The 1.0 series ships. Debian's archive first sees ferite 1.0.0.1+rc2 on 12 March 2005, and the project's release notes record 1.0 final as a small bug-fix release over rc2, fixing iferr blocks inside loops and class and namespace modification of existing types. The 1.0 release candidates had rewritten the manual, added a date module, renamed constructors to constructor, and introduced closures and the using block syntax
2006
Version 1.1.0pr is reported as released on 7 January 2006, opening the 1.1 maintenance series
2009
The last burst of upstream work: February 2009 brings automatic detection and use of the Boehm garbage collector (libgc), the change that prompted the bump to 1.1.12, and a compile cache lands in March 2009. The final SourceForge tarball, 1.1.17, is posted on 22 October 2009
2010
The source is moved to GitHub - the darkrock/ferite repository is created on 23 May 2010 - with more than a dozen module repositories (JSON, SOAP, memcached, Cairo, Pango, Selenium, a web framework) following in June 2010
2011
The last commits to the core engine repository are made in July 2011, fixing stack reallocation in the interpreter's execute records
2024
FreeBSD deletes lang/ferite from its ports tree on 31 October 2024, marking it abandonware and outdated - last release in 2009, with the ported version dating from 2005 - closing a 23-year run in the collection

Notable Uses & Legacy

Cention AB

The Swedish customer-service software company hosted one of ferite.org's official mirrors and built on the language heavily. Its GitHub organisation carries a ferite-1.1.18 fork described as the stable version of the language, an Apache module (mod_ferite), and application-specific modules for the Cention Suite covering email, exports, reports and statistics - some with commits as late as 2016 to 2019, long after upstream development had stopped.

Debian GNU/Linux

Ferite was packaged for Debian from an intent-to-package bug filed in January 2002; the archive holds source packages for 0.99.4-4 and for 1.0.0.1+rc2-1, which first appeared in March 2005.

FreeBSD ports

lang/ferite was added on 14 October 2001 and survived in the ports tree until 31 October 2024, depending only on libpcre and libxml2 - a small dependency footprint that matched the engine's embedded-first design goals.

Ferite.framework for Cocoa

An Objective-C framework for embedding the engine into Mac OS X applications, reported as work in progress in 2003. The project's SourceForge listing records Objective-C alongside C as an implementation language, and later build scripts contain explicit Mac OS X version checks.

The ferite module ecosystem

The engine's C extension API was exercised by a long list of modules maintained by Chris Ross and contributors - XML and XSLT, database access, zip, SOAP, JSON, memcached, Cairo and Pango drawing, Selenium driving, and a web framework - which together are the clearest surviving record of what people actually built with ferite.

Language Influence

Influenced By

Running Today

Run examples using the official Docker image:

docker pull
Last updated: