Citrine programming language
a GaborSoftware product


a simple
programming language
for everyone

play learn docs download

Easy to Learn
Citrine is easy to learn because of it's minimalism. There are just a couple of rules you have to memorize and then you can already write your first program.

Readable
Citrine aims to be very human readable. The language reads like some sort of pidgin language. You can use Citrine to write code that mimics natural language to quite a comfortable degree.

Accessible
Citrine fully support UTF-8 so you can write code in your native language using your native character set. Basic Citrine commands can be translated so you don't have to mix English and your native language if you don't like that.

First Steps

Let's write a little program to greet the world. If you don't want to install Citrine you can do this using the playground.

Pen write: 'Hello World'.

This little piece of code will print the text 'Hello World' to the console. After running the program, you will see:

Hello World

Look quite natural right? Now let's write a little program that counts to 10:

0 to: 10 step: 1 do: { :number
    Pen write: number.
}.

This program will print a sequence of numbers:

012345678910

While it contains a bit more symbolism, like the { and } the code is still quite readable. It orders the system to count from 0 to 10 with a step size of 1 and print every number on the screen.

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:

# this a comment
# it starts with #.

Messages

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 (.) . To assign the result of this operation to a variable we use :=. Example:

x := 3 factorial.

Now, variable x equals 6. You can chain messages like this:

x := 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:

x := 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.

Note that spaces matter, you have to write 3 + 7 not 3+7.

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: 'A', write: 'B'.

String Interpolation

Citrine supports basic string interpolation like this:

var myName := 'Gabor Software'.
Pen write: 'Created by $$myname '.

The reason why Citrine uses the double dollar sign is to allow developers to quickly use variables in strings (just double tap the dollar) without requiring every regular dollar to be escaped or to require a more complicated syntax. The dollar sign has been chosen because it's a familiar string interpolation symbol also used in PHP and Java.

Control Flow

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: 'dream on!'.
}.

To test for equality we use = not ==. Example:

( answer = 3 ) ifTrue: {
 Pen write: 'That\'s correct!'.
}.

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.

Exceptions

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!

Creating your own objects

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 each parameter of a block starts with a colon (:).

Properties (Me and My)

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

animal := Object new.
animal on: 'name:' do: { :n
 my name := n.
}.

Since properties are private, other objects can't access it. If you want to make a property (like name) available, you have to add a method that returns it, like this:

animal on: 'name' do: {
 ^ my name.
}.

If you want to send a message to the current instance you use the me keyword. So, for instance, say we have a method called 'sleep' and we wish to make an alias for it called takeNap, we could write something like:

animal on: 'takeNap' do: {
 me sleep.
}.

Here, on receiving the message takeNap, the animal will call its internal method sleep (sending a message to itself using the me keyword).

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.

Prototypes

To reuse code, Citrine uses prototypical inheritance. Let's create a cat from the animal object:

cat := animal new.
cat name: 'Diva'. 

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 (`).

cat on: 'name' do: {
 ^ 'your royal highness '
  + (me `name).
}.

If you now ask the cat for its name, it will point out you should address her with 'your royal highness Diva.' instead of just 'Diva'.

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. }.

Generic responses

By default, if an object does not know how to respond to a certain message it just does nothing and returns a reference to itself. This applies even to Nil, so in Citrine you don't have to worry about NullPointerException issues like in Java.

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

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

You can also craft dynamic messages using a string and an array like this:

str := 'between:and:'.
Pen write: (3 message: str arguments: (Array < 2 ; 8)), brk.

Functions & Scoping

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

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

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'.

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

Plugins

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 the 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.

Citrine plugins written by the Citrine community:
Plugin: JSON (by Jake Russo)
Plugin: CURL (by Jake Russo)

HTTP Plugin

Citrine ships with a CCGILIB based 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:

mods/{plugin}/libctr{plugin}.so

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

mods/request/libctrrequest.so

you can use the Request object like this:

#blog.ctr?search=eastereggs
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 we put the following code in storm.ctr:

#Citrine Storm Server Demo storm.ctr
#Put your Citrine scripts in /var/www/htdocs/...
#To kill this server
#kill $(cat /var/run/storm.pid)
Pen write: 'Start Citrine Storm Server v1', brk.
Request host:'localhost' listen:4000 pid:'/var/run/storm.pid' 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.
}.

NGINX Configuration

So, to begin with we have to bring up the Citrine SCGI Storm Server like this:

ctr storm.ctr

You'll now see a welcome message like this:

Start Citrine Storm Server v1

Now it's time to configure NGINX to use Storm Server, open nginx.conf:

vi /etc/nginx/nginx.conf

(note: the path of the nginx configuration file can be different depending on your OS)

and add:

location ~ \.ctr$ {
        try_files $uri $uri/ =404;
        scgi_pass 127.0.0.1:4000;
        include   scgi_params;
}

Now restart NGINX and you're done! You can now add your Citrine pages (using .ctr extension) in /var/www/htdocs/ or whatever folder you specified in storm.ctr. Have fun!



Written by Gabor de Mooij,
📪 mail at gabosoftware ​dot ​nl



Served by Citrine

BACK TO TOP