Citrine Programming Language

-- Because someone has to read your code --

Welcome to the Citrine® Programming Language Website. Here you'll find the reference manual page (API), the roadmap and updates about the progress of the language.

Citrine is an easy-to-learn general purpose programming language for UNIX®-like operating systems. Citrine is inspired by the Kayian School of Object Orientation. The Citrine Programming Language focuses on simplicity and emphasizes maintainability through readability. Simplicity is achieved by syntactical and conceptual minimalism. It's fully open source, licensed BSD:

Citrine 0.5 OpenBSD Binary Distribution AMD64 TARGZ
Citrine Latest Source Code (github).

View Test Reports

discuss on Google Groups discuss on reddit

What does it look like?

A slightly over-engineered 'hello world' program:

Butler := Object new.
Butler on: 'greet:' do: { name |
	Pen write: 'Welcome ' + name.
james := Butler new.
james greet: 'visitor'.

Run the program:

$ ctr hello.ctr

This program will generate the following output:

Welcome visitor

Obviously this code could have been written much simpler, but that would reveal less of the language.


Citrine combines lots of ideas from various programming languages like Ruby, Smalltalk, JavaScript and C.

The basics

In Citrine, everything is an object. You write a program by making these objects talk to each other, by sending messages.

There are 5 literal objects:

Strings begin and end with a single quote (').

This is what a comment looks like:

# Hello this line is a comment, because
# it starts with a #.

There are three kinds of messages. A unary message is a message without any arguments:

3 factorial.

Here we send the message 'factorial' to 3, this expression will return 6 ( 3 * 2 * 1 ). Note that each line of code should end with a period (.) . You can chain messages like this:

3 factorial factorial.

This line will first send the message 'factorial' to the number 3. Then, it will send the message 'factorial' to the result of the previous operation (6), the final result will be 720.

A binary message uses infix notation:

3 + 7.

This code sends the message '+ 7' to 3, the result will be 10. Every message that's only one unicode character long is considered to be a binary message. A binary message always takes one argument: the object that follows after the message.

So, for instance: + - / * < > and = are all binary messages.

Finally, there are keyword messages taking one or more arguments:

3 between: 1 and: 5.

Here we send the message 'between: 1 and: 5' to number 3. This expression will return True. Here is another example:

Pen write: 'Hello world!'.

This will print 'Hello World!' on the screen. We send the message 'write: hello world!' to the Pen object and that object will print the message on the screen.

You can chain multiple keyword messages using a comma:

Pen write: 'Hello', write: 'world!'.

Citrine does not need a special syntax for loops or conditions. To create a loop, send 'times' to a number:

3 times: {\ Pen write: 'Ho!'. }.

Likewise, to create a condition, send 'ifTrue' (or 'ifFalse') to a Boolean:

( money < price ) ifTrue: {\ Pen write: 'not enough money!'. }.

You can create your own objects by sending the 'new' message to Object.

myObject := Object new.

Once you've created a new object, you can make it respond to messages (adding methods) like this:

myObject on: 'getBeverage' do: {\
	^ 'Coffee'.

As you can see, the second argument of this message is a literal block of code returning a string (^ means return, it's easy to remember because it takes the form of a little arrow pointing upwards!). If you do not return anything within a block of code, a reference to the object itself will be returned (so you can chain messages).

myObject on: 'getBeverageFor:' do: { forPerson |
	var b := 'coffee'.
	(forPerson = 'Picard') ifTrue: {\ 
		b := 'Earl Grey'. 
	^ b.

Note that a block without parameters (previous example) uses a \ instead of a |. This is also easy to remember: think of the arguments as books supporting the pipe like a bookend, if they are removed the pipe falls over and leans on the curly opening bracket.


You can treat a block of code like a function, just send the message run to a block and it will execute the code within. If you need parameters use: applyTo instead.

{\ ^9. } run. #yields 9
{ a | ^ a * a. } applyTo: 6. #yields 36
{ a b | ^ (a + b). } applyTo: 3 and: 4. #yields 7
{ a b c | ^ (a + b + c). } applyTo: 3 and: 4 and: 1. #yields 8


Citrine uses dynamic scoping. To declare a new variable use:

var a := 1.

Here, we assign the value 1 to variable a in the current scope, it will exist as long as the current block of code runs, and it will be visible to all blocks of code called during that time.

{\ var q := 1. {\ Pen write: q. } run. } run. #prints 1.
{\ var q := 1. {\ q := 2. Pen write: q. } run. } run. #prints 2.
{\ var q := 1. {\ x := 2. Pen write: x. } run. } run. #Not allowed x is not defined
f := {\ q := 2. }. {\ var q := 1. f run. Pen write: q. } run. #prints 2

As you might have noticed, only in global scope you're allowed to omit the var keyword.


To create a closure, bind the in-function variables explicitly to the block object, like this:

multiplier := { m | ^ { x | ^(my f * x). } set: 'f' value: m. }.
double := multiplier applyTo: 2.
q := double applyTo: 9. #yields 18

Higher order functions

Here we create a higher order function mapper that takes an array and a function and applies the function to every element in the array:

addition := { x |
	var f := { y | ^ (my x + y). } set: 'x' value: x. 
	^ f.
mapper := { array func |
	var mapping := { i | my q put: (my f applyTo: (my q at: i)) at: i. } 
	set: 'q' value: array,
	set: 'f' value: func.
	(array count) times: { i | mapping applyTo: i. }.	
a := Array <- 1; 2; 3.
mapper applyTo: a and: (addition applyTo: 100). #101 102 103

Properties (Me and My)

All properties of an object are private, you can access them using the my keyword.

myObject on: 'setBeverage:' do: { b |
	my beverage := b.

On the other hand, if you want to send a message to the current instance you use the me keyword.

myObject on: 'getBeverage' do: {\
	me prepareBeverage. #sends message to self

The keywords me and my always refer to the objects to which they belong. If you invoke a block of code by sending the run or applyTo: message, me and my will refer to the block itself.


To reuse code, Citrine uses prototypical inheritance. Suppose you have a dog and a cat, both can respond to the message 'eat', so to avoid duplicate code we put that response in another object called Animal and then create dog by sending the message new to Animal instead of Object.

Animal := Object new.
Animal on: 'eat' do: {\ 
	Pen write: 'munch! munch!'.
dog := Animal new.
dog eat.

The new message will create a new object, setting the prototype link to the original one. To invoke an overridden method, prefix the message with a backtick (`).

dog on: 'eat' do: {\ 
	me `eat. #Animal will respond

Malleable Objects

You can extend existing objects, for instance, to make numbers respond to × :

#let's extend the number object with a unicode times equivalent!
Number on: '×' do: { b |
	me times: b. 
7 × { i | Pen write: i. }.

Break and Continue

To break out of a loop send the 'break' message to a boolean, like this:

5 times: { i |
	Pen write: i.
	(i > 2) break. #after 2

To skip the remainder of a block in a loop and proceed to the next iteration use 'continue'.

(i > 2) continue.


To catch an exception, you need to associate a catch block with your code block:

{\ #throw an exception:
	thisBlock error: 'oops!'.
} catch: { err |
	Pen write: err.
}, run.

Don't forget the comma right after the catch block! You want to say 'run' to the first code block, not the catch block!

Generic responses

You can make objects respond to arbitrary messages, in some languages this is known as 'magic methods'.

echo := Object new.
echo on: 'respondTo:' do: { 
	sound |
	2 times: {\ Pen write: sound. }. 
echo ho!. #prints ho!ho!

Unicode Symbols

Citrine frequently uses Unicode symbols: (a ≤ 3). However, since most keyboards lack a '≤' key, the Citrine Lexer will automatically convert : '<=' to '≤'. The Lexer automatically converts the following character sequences:


Spaces matter

In Citrine, spaces matter, for instance:

priority + 3

should mean something different (send binary message + with argument 3 to object priority) than:

priority +3

i.e. send unary message +3 to priority. However, to avoid confusion, the following binary messages have 'special priority': + - / * = != > < >= <= and <-. This makes it possible to do (3+3) instead of having to keep track of the spaces: (3 + 3). Unfortunately this also means unary messages are not allowed to begin with one of these characters.

The == symbol

Because Citrine uses := for assignment, it makes sense that = is used for comparisons. However, because there are so many languages out there using ==, Citrine accepts == as an alias for =.

Template Syntax

Citrine supports PHP-like template syntax using <? and ?>. To use Template Syntax, begin your document with ?> start a code section with: '<?.'. Mind the dot, it ends the 'template' string. Without the dot you can chain additional Pen commands like 'write'.

	<?. Pen write: 'Citrine starts here!'. ?>	


To keep Citrine clean and small we use plugins (thus adhering to the UNIX philosophy of doing only one thing and doing it well). To load a plugin just begin to send messages to object provided by the plugin. As soon as you start 'talking' to the object, Citrine will attempt to load it from your plugin folder. Take a look at the HTTP plugin below for an example.

HTTP Plugin

Citrine ships with a HTTP plugin that allows you to deal with GET and POST requests. There's no need to explicitly load the plugin, just start talking to an object and it gets loaded automatically. Plugin path format:


for instance the HTTP Request plugin provides a Request object, so the path is:


you can use the Request object like this:

s := Request get: 'search'. #autoloads Request plugin
Pen write: ('searching for: '+s). #searching for eastereggs

More examples:

list := Request getArray: 'orders[]'. #GET an array of items
post := Request post: 'message'. #get a POST var
list := Request postArray: 'orders[]'. #array from POST
file := Request file: 'avatar'. #returns Array (0 = temp file, 1 = name)

Storm Server

Storm Server is a CCGILib based SCGI server for Citrine for hi-traffic web applications, to start an instance of Storm:

#Citrine Storm Server Demo
#Put your Citrine scripts in /var/www/htdocs/...
#To kill this server
#kill $(cat /var/run/
Pen write: 'Start Citrine Storm Server v1', brk.
Request host:'localhost' listen:4000 pid:'/var/run/' callback: {\
	#Now set the content type and any other headers
        Pen write: 'Content-type: text/html\n\n'.
	#Load the script file
	var fname := Command env: 'DOCUMENT_URI'.
        var script := File new: '/var/www/htdocs'+fname.
        #Run it!
	script include.

Written by Gabor de Mooij,
📪 mail at gabordemooij ​dot ​com

Served by Citrine