Fancy is a dynamic, concurrent, pure object-oriented general purpose programming language inspired by Smalltalk, Ruby, and Erlang. It runs on Rubinius.
The goal is to create a language implementation that is easy to understand and improve, even for people new to implementing programming languages.
Fancy is a self-hosted language. That means, its compiler is written in Fancy itself. Apart from the compiler, Fancy's standard library is mostly written in Fancy, too. The standard library is a good starting point if you want to get a better feel for the language and its built-in classes and methods, once you've mastered the fundamental semantics and syntax. You can view the standard library classes on GitHub here.
OK, enough said, let's get started with Fancy's basic concepts.
Fancy is heavily inspired by Smalltalk, a pure object-oriented, message passing dynamic programming language, developed at XEROX PARC in the 1970s and 1980s.
The core idea of Smalltalk (and thus Fancy) is the concept of message sending (also called message passing). Fancy code interacts by sending messages to objects and getting responses while doing so. You can think of methods as message handlers for incoming messages on objects. In Fancy, as in Smalltalk and Ruby, every value is an Object. There is no distinction between so-called value types (or primitive types) and reference types.
In Fancy nearly all operations are done via message sends. While Fancy does have syntax for class & method definitions, importing files and so on, most of them are just syntactic sugar for equivalent message sends to objects.
Let's have a look at how this looks syntactically.
No arguments:
object message_nameSingle argument:
object message_name: argIs a message send to object with a message called message_name:
and an argument arg. As in Smalltalk (or Objective-C, which is
inspired by Smalltalk), messages are keyword-based and so are
methods. So, if you have multiple arguments in a message send, each
argument is preceeded by a keyword (usually describing the argument).
Multiple arguments:
object foo: arg1 bar: arg2 baz: arg3The above shows a message send with three arguments for the
foo:bar:baz: message. If object, its class or any superclass
defines a method with the same name, it will get executed with the
given arguments. If not, it will look for a method called
unknown_message:with_params: within objects inheritance chain (as with
Ruby's method_missing).
Lets look at a more real-world example:
[1,2,3] each: |x| {
x println
}This piece of code shows a message send to a literal Array consisting
of the three number values 1, 2 and 3. The each: method
expects a Block object (or actually, something callable - it needs
to implement the call and call: methods). It will call: the
argument given to it with each element in the Array.
The |x| { x println } thing is a Block literal. Blocks are just
normal objects but with built-in syntax (like strings, numbers and
many more). As in Ruby, anything between the pipes (||) are arguments
to the block and the code between the curly braces is the body of the
block. Blocks are used as anonymous methods (also called lambda
functions) and closures within Fancy. They are first-class values
in the language, like any other object, and can be passed around to
any method. In Fancy all control structures are implemented with
blocks. But we'll get to that in a minute.
First off, let's fully explain that code above. As mentioned, the
each: method takes something that implements call: and passes it
each element in the array in turn. You can think of each: being used
as an iterator. In terms of the language, each: is just a method
like any other though.
The println send to x causes it to be displayed on
*stdout*. Here's the implementation of println:
def println {
# *stdout* is a dynamic variable
# we'll discuss these later in detail
*stdout* println: to_s
}You can find the code in lib/object.fy within Fancy's root source directory or just click here to view it on Github.
As mentioned before, Fancy hardly has any built-in control structures in contrast to most other programming languages out there. It's directly inspired by Smalltalk here in that all the usually built-in control flow structures are implemented with Blocks and their ability to access variables outside of their scope (and preserve them).
Let's look at the most common control structures found in other programming languages.
If/Else:
x < y if_true: {
"x is smaller than y" println
} else: {
"x is NOT smaller than y" println
}The else: part can be ommited, as there's also a method if_true:
defined apart from if_true:else:. You can also write it the
following (although longer) way:
if: (x < y) then: {
"x is smaller than y" println
} else: {
"x is NOT smaller than y" println
}
# there's also this additional way of doing something conditionally:
{ "x is smaller than y" println } if: (x < y)While:
x = 0
{ x < 10 } while_true: {
x println
x = x + 1
}
# or:
x = 0
while: { x < 10 } do: {
x println
x = x + 1
}Endless loop:
loop: {
# do something here and possibly return when done
}For-loop:
10 times: |i| {
i println # will print 0 - 9
}
# or:
0 upto: 9 do: |i| {
i println # same here.
}