Home

Daniel Șuteu edited this page Jul 23, 2016 · 341 revisions
Clone this wiki locally

BOOK

The Sidef Programming Language: http://trizen.gitbooks.io/sidef-lang/content/

Introduction

Sidef is a modern, yet experimental, dynamic, functional / object-oriented programming language, focusing on simplicity, readability and elegance, taking the best from languages like Ruby, Go, Perl 6 and Julia.

Installation

To install Sidef, download the latest version, unzip it and follow the installation steps:

$ perl Build.PL
# ./Build installdeps
# ./Build install --install_path script=/usr/bin

For packaging Sidef, run:

$ perl Build.PL --destdir "/my/package/path" --installdirs vendor
$ ./Build test
$ ./Build install --install_path script=/usr/bin

Alternatively, it can be installed from CPAN, by executing the following command:

$ cpan Sidef

If the installation succeeded, you should be able to run the sidef command:

$ sidef -h

Run Sidef without installing it

It is possible to run Sidef without having to install it. If you're using a Unix-like OS, all you need to do is to execute the following commands:

$ wget 'https://github.com/trizen/sidef/archive/master.zip' -O 'master.zip'
$ unzip 'master.zip'
$ cd 'sidef-master/bin/'
$ perl sidef -v

The above lines will download and unpack the latest version of Sidef and will execute the bin/sidef script which will print out the current version of the language.

To execute a Sidef script, run:

$ perl sidef ../scripts/draw_a_cuboid.sf

You can, also, add the following alias to your ~/.bashrc:

alias sidef="/path/to/bin/sidef"

Creating the first Sidef script

A Sidef script can be written in any text editor and, by convention, it has the .sf extension.

The content of a simple Hello World program looks like this:

#!/usr/bin/sidef

say "Hello, 世界";

If we save the content in a new file called hello.sf, we can execute the code by running:

$ sidef hello.sf

Real-world code

Before taking a closer look at the syntax of the language, let's take a brief look at how a real-world program might look like. The following program defines the Bitmap class and generates a PPM file with a color palette:

subset Int   < Number {|n| n.is_int  }
subset UInt  < Int    {|n| n >= 0    }
subset UInt8 < Int    {|n| n ~~ ^256 }

struct Pixel {
    R < UInt8,
    G < UInt8,
    B < UInt8
}

class Bitmap(width < UInt, height < UInt) {
    has data = []

    method fill(Pixel p) {
        data = (width*height -> of { Pixel(p.R, p.G, p.B) })
    }

    method setpixel(i < UInt, j < UInt, Pixel p) {

        subset WidthLimit  < UInt { |n| n ~~ ^width  }
        subset HeightLimit < UInt { |n| n ~~ ^height }

        func (w < WidthLimit, h < HeightLimit) {
            data[w*height + h] = p
        }(i, j)
    }

    method p6 {
        "P6\n#{width} #{height}\n255\n" +
            data.map {|p| [p.R, p.G, p.B].pack('C3') }.join
    }
}

var b = Bitmap(width: 125, height: 125)

for i,j in (^b.height ~X ^b.width) {
    b.setpixel(i, j, Pixel(2*i, 2*j, 255 - 2*i))
}

var file = File("palette.ppm")
var fh = file.open('>:raw')
fh.print(b.p6)
fh.close

The following image is produced:

palette

Another example is the implementation of the LCG algorithm, illustrating modules, and class inheritance.

module LCG {

  # Creates a linear congruential generator with a given seed.
  class Common(seed) {
     has r = seed
  }

  # LCG::Berkeley generates 31-bit integers using the same formula
  # as BSD rand().
  class Berkeley < Common {
    method rand {
      self.r = ((1103515245 * self.r + 12345) & 0x7fff_ffff);
    }
  }

  # LCG::Microsoft generates 15-bit integers using the same formula
  # as rand() from the Microsoft C Runtime.
  class Microsoft < Common {
    method rand {
      self.r = ((214013 * self.r + 2531011) & 0x7fff_ffff);
      self.r >> 16;
    }
  }
}

var lcg1 = LCG::Berkeley(1)
say 5.of { lcg1.rand }

var lcg2 = LCG::Microsoft(1)
say 5.of { lcg2.rand }

Output:

[1103527590, 377401575, 662824084, 1147902781, 2035015474]
[41, 18467, 6334, 26500, 19169]

A last example, implementing a naive algorithm that finds the longest common substring from two given strings, illustrating the declaration of functions with typed parameters, the gather/take construct and the set-intersection operator (&).

func createSubstrings(String word) -> Array {
  gather {
    for i,j in @(0 .. word.len).combinations(2) {
        take(word.substr(i, j-i))
    }
  }
}

func findLongestCommon(String first, String second) -> String {
    createSubstrings(first) & createSubstrings(second) -> max_by { .len }
}

say findLongestCommon("thisisatest", "testing123testing")    #=> "test"

Syntax

The syntax of Sidef looks very much like the syntax of Ruby or JavaScript, but we'll see that there are some important differences regarding the semantics.

All operators have the same precedence, which is controlled by the lack of whitespace between the operands.

1+2 * 3+4   # means: (1+2) * (3+4)

In the above example, the lack of whitespace between 1, + and 2, classifies the operation as a distinct expression.

The implications are the following:

var n = 1 + 2       # incorrect -- it means: (var n = 1) + 2
var n = 1+2         # correct
var n = (1 + 2)     # correct

When no precedence is defined, the order of operations is from left to right:

1 + 2 * 3       # means: ((1 + 2) * 3)

On the other hand, when too much precedence is defined, the order is from right to left:

1+2*3           # means: (1 + (2 * 3))

Starting with Sidef 2.30, the precedence can be controlled by backslashing or preceding the operator with a dot.

1 + 2 \* 3      # means: ((1 + 2) * 3)
1 + 2 .* 3      # same as above

The infix backslash (\) removes any leading or trailing whitespace at that current position and it's useful for expanding expressions on multiple lines:

say "abc".uc      \
         .reverse \
         .chars

is equivalent with:

say "abc".uc.reverse.chars

Keywords

Initially, the language was designed without any keywords. However, as it turned out, keywords can simplify greatly the writing of code, at the cost of not having a variable with the same name as that of a keyword. So, now, we have the following list of keywords:

var             # declare a semi-dynamic variable, with a stack
local           # declare a local dynamic variable
func            # declare a function
class           # declare a class
module          # declare a module
subset          # declare a subset
struct          # declare a structure
const           # declare a run-time static constant
static          # declare a run-time static variable
define          # declare and assign a compile-time static constant
enum            # declare and assign a list of constants with distinct numbers

eval            # eval some arbitrary Sidef code in the current scope
warn            # print an warning to STDERR followed by the current file name and the line number
die             # raise an error followed by the current file name and the line number
read            # read from standard input a type of data (e.g.: read(Number))

assert          # terminate the program if the argument is false
assert_eq       # terminate the program if two arguments are not equal to each other
assert_ne       # terminate the program if two arguments are equal to each other

nil             # 'not initialized' value
true            # boolean representation for a true value
false           # boolean representation for a false value

if              # 'if' condition
while           # 'while' condition
loop            # infinite loop
for             # 'for' loop
foreach         # alias for 'for'
given           # 'given/when' statement
with            # 'with' statement
gather          # 'gather/take' statement
continue        # fall-through in a 'given/when' statement
return          # stop anything and return a value from a function
break           # break the current loop
next            # go to the next loop iteration

include         # load a Sidef module
import          # import a list of identifiers from a given module

Unary operators

Unary operators are those operators which can take only one argument. In Sidef we have the following unary operators:

+               # same object
-               # the negative value of a number
++              # increment the value
--              # decrement the value
~               # the 'not' value of a number
\               # takes reference to a variable
*               # dereferences a reference
:               # initializes a new hash
!               # the negation of a Boolean value
^               # an exclusive range (from 0 to n-1)# square root of a number

Examples:

var x = 42;

say +x;            #  42
say -x;            # -42
say ~x;            # -43
say !true;         # false
say √49;           # 7
say ^10;           # RangeNum(0, 9, 1)
say x++;           # 42
say ++x;           # 44
say --x;           # 43
say x--;           # 43

# Creating a new hash with ':'
var hash = :(
     key => 'value',
)

var ref = \hash         # referencing
var original = *ref     # dereferencing

Built-in types

This objects deal with the data types in Sidef and implements the useful methods visible to the user.

File
FileHandle
Dir
DirHandle
Arr Array
Pair
MultiArray MultiArr
Hash
Str String
Num Number
RangeStr RangeString
RangeNum RangeNumber
Complex
Math
Pipe
Ref
Socket
Bool
Sys
Sig
Regex Regexp
Time
Perl
Sidef
Object
Parser
Block
Backtick
LazyMethod

Ignoring the performance differences, a string "" is just the same thing as String().

[1,2,3];                   # same as: Array(1,2,3);
                           # same as: 1..3;

true;                      # same as: Bool(1);
false;                     # same as: Bool(0);

/^some[regex]?/i;          # same as: Regex('^some[regex]?', 'i');
:(key => "value");         # same as: Hash("key", "value");
"first":"second";          # same as: Pair("first", "second");

Method invocations

A method is a function defined for a specific type of object. For strings, we have a method called length, which differs from the method with the same name that is defined for array-type objects.

"string".length         #=> 6
[1,2,3].length          #=> 3

In many languages, method invocations require parentheses, but in Sidef the parentheses are optional when we call a method without any argument.

Methods

For calling a method, we have the following notation:

obj.method_name(arg)

For convenience, methods can also be invoked using the following prefix notation:

::method_name(obj, arg)

The prefix and postfix notations can be used interchangeably:

::log("string".length)    # means: "string".length.log
::length("string").log    # =//=
::log(::length("string")) # =//=

There are two method-invocation operators in Sidef: . and ->.

20 + 5.sqrt    # means: (20 + (5.sqrt))
20 + 5->sqrt   # means: ((20 + 5).sqrt)

-> will make everything, from the left side of it, a single expression and applies the method on the result returned by the expression, while . will just apply the method on the object which precedes the dot.

There is also support for calling a method which its name is not known until at run-time, which can be any expression that evaluates to a string:

say ( 50.(["+", "-"].rand)(30) )    # prints 20 or 80

If a method is not found for a given object, Sidef will throw a run-time error.

Variables

Variables are commonly declared using the var keyword:

var num = 42
var str = "42"
var bool = true

Variable types

In Sidef exists four types of variables: static variables, lexical variables, global varialbes and local variables.

STATIC VARIABLES

This type of variables are static, block-scoped and initialized only once.

static x = 42   # sets the static x to 42
say x           # prints the static value of x

LEXICAL VARIABLES

This kind of variables are dynamic, but statically block scoped. This is the usual way of declaring variables in Sidef.

var x = 42    # sets the lexical x to 42
say x         # prints the lexical value of x

GLOBAL VARIABLES

Global variables are declared at the top-level of the current namespace. They can be accessed from everywhere, anytime. However, try to avoid using them, unless you really don't have any better alternative.

global x = 42     # sets global x to 42
say x             # prints the global value of x

LOCAL VARIABLES

Local variables (also known as "dynamically scoped variables") are used to localize array/hash lvalues or global variables to a limited scope.

global x = 42    # sets the global x to 42
do {
   local x = 100     # localizes x inside this block to 100
   say x             # prints the local value of x
}
say x            # prints the global value of x (42)

Variable scoping

All variables, including functions and classes, are block scoped in the following way:

var x = 'o'

do {
    say x              # o
    var x = 'm'
    say x              # m
    do {
        say x          # m
        var x = 'b'
        say x          # b
    }
    say x              # m
}

say x                  # o

Declaring multiple variables on the same line works exactly like expected:

var (x, y, z) = (3.14, false, "foo")

We can, also, declare variables with some default values:

var (x, y=755, z=777) = (666, 655)

say x        # prints: 666
say y        # prints: 655
say z        # prints: 777

Slurpy variables:

Slurpy (or greedy) variables are a special type of variables which can be initialized with a list of values, creating automatically a container to hold the data.

var *arr = (1,2,3)    # creates an Array
say arr               # prints: [1,2,3]

var :hash = (a => 1, b => 2)   # creates an Hash
say hash                       # prints: Hash(a => 1, b => 2)

Manipulating variables

Any method applied to a variable is applied to the object stored inside the variable.

var x = 'sidef'
say x.uc        # prints: `SIDEF`
say x           # prints: `sidef`

Special ! at the end of a method changes the variable in-place (almost like in Ruby):

var x = 'sidef'
x.uc!            # notice the `!`
say x            # prints: `SIDEF`

Appending the = sign at the end of arithmetic operators, the variable will be changed in place:

var x = 5
x += 10            # adds 10 to "x"
say x              # prints: 15

Special variables

Currently, there are only two real predefined variables: ARGV and ENV.

ARGV.each { |arg|
   say arg
}

say ENV{:HOME}

This variables are defined only if they are used anywhere inside the program.

Topic variable

The special topic variable (_) is declared at compile-time and every block of the script. You may not see its real name very often, because it has been overtaken by the elegant unary dot (.) operator.

[25,36,49].map {.sqrt} \
          .each{.to_s.say}

.sqrt really means _.sqrt, and .to_s.say means _.to_s.say.

The code works like this: the map method will iterate over the anonymous array and will assign each number to the topic variable _, then it will run the block and will store the returned value inside a new array. When the iteration is complete, the map method will return the new array, on which we call the each method with another block as the argument.

The each method, as the map method, will iterate over the array and will run the block for each element of the array, which will get assigned to the topic variable _, on which we call the to_s method, followed by say, converting the number to a string then printing it to the standard output.

Magic variables

Sidef's magic variables are directly bound to Perl's magic variables.

local $/ = nil        # changes the input record separator
local $\ = "\n"       # change the output record separator
local $, = "\n"       # changes the field record separator
say $^PERL            # prints the path to perl executable
say $^SIDEF           # prints the path to sidef executable

File-handle constants

This constants look like variables, but are actually file-handles.

STDERR.print("Some error!")
STDOUT.print("Some output...")
STDIN.readline()   # reads a line from the standard input

Another interesting file-handle is the ARGF which will read lines from argument-files or from standard-input when no argument has been specified.

Here is the implementation of a very basic cat-like program:

ARGF.each { |line|
    print line
}

Like in Perl, there is also the DATA file-handle which will point to the data stored after the __END__ or __DATA__ tokens.

DATA.each { |line|
    say "=>> #{line.chomp}"
}

__DATA__
here are
some data
lines

Constants

Sidef implements three kinds of constants (actually, five if we count the static variables and the function-based constants).

const

The traditional way of creating constants in Sidef, is by using the const keyword:

const pi = 3.14
say pi                 # prints: 3.14
pi = 3                 # compile-time error: can't modify non-lvalue constant

The constants are created lexically at run-time, but are initialized only once.

define

This keyword will define a compile-time evaluated constant and will point directly to the object at which it evaluated to.

define PHI =   (1.25.sqrt + 0.5)
define IHP = (-(1.25.sqrt - 0.5))

say (PHI**7 - IHP**7 / PHI-IHP)

This type of constants are the most efficient ones.

enum

enum will automatically declare and assign a list of constants with ascending numeric values (starting with 0):

enum |Black, White|
say Black             # prints: 0
say White             # prints: 1

Alternatively, we have the possibility to specify an initial value, which will get incremented after each declaration, by calling the method inc.

enum |α="a", β|
say α             # prints: 'a'
say β             # prints: 'b'

static

To declare the variables only once, we can use the static keyword, which will assign to the variable only once. However, it can be changed after declaration. Behaves exactly like the state keyword from Perl.

func myConst(x) {
    static a = x   # assigned only once
    return a       # returns the value of 'a'
}(42)

say myConst()        # prints 42
say myConst(7)       # prints 42, regardless of arguments

func

Another way of creating constants is to use a function with no parameters:

func Φ { Math.sqrt(1.25) + 0.5 }
say Φ()                # prints: 1.61803398874989484820458683436563811772

Variable references

Like other programming languages, Sidef is capable of taking references to variables.

var name = "sidef"
var ref = \name        
var original = *ref

Variable references are useful when passing them to functions (or methods) for assigning values.

func assign2ref (ref, value) {
    *ref = value
}

var x = 10
assign2ref(\x, 20)
say x                 # prints: 20

Blocks

In Sidef, a block of code is an object which encapsulates one or more expressions. The block is delimited by a pair of curly braces ({}).

var block = {
    say "Hello, World!"
}

Block calls

Being an object, we have the possibility to call methods on it:

block.run(1,2)     # this runs the block as an expression
block.call(1,2)    # this calls the block as a function

The .call method treats the block as a function, while the .run method runs the block as an expression, without taking into account named parameters or multiple dispatch (the features that functions have), therefore .run is slightly faster.

Block iterators

Blocks are also used as arguments to many built-in methods to create iterators or to compute values.

{ print "Sidef! " } * 3        # Prints "Sidef! Sidef! Sidef! "
5.times {|x| print x }         # Prints "12345"
[1,2,3].sort {|a,b| b <=> a}   # Returns a new array: [3,2,1]

Block parameters

For declaring block parameters, Sidef borrows Ruby's way of doing this, by using the |arg1, arg2, ...| special syntax.

{  |a, b|

   say a            # prints: 1
   say b            # prints: 2

}(1, 2)

Default block parameter values

We can also specify default values for block parameters. This can be done by using the syntax: arg=value.

say { | a=3, b=4 | a + b }(9)        # prints the result of: 9 + 4

The default value can be any expression:

say { | a=1/2, b=(a**2) | a + b }(5)      # prints the result of: 5 + 5**2

Lazy evaluation

Lazy evaluation is a very common feature in functional programming languages and provides an way to delay the evaluation of an expression until the result is actually needed.

By default, Sidef is an eagerly evaluated language, but it still supports a form of lazy evaluation, which is provided by the method Object.lazy():

say (^Inf -> lazy.grep{ .is_prime }.first(10))     # first 10 primes

The .lazy method returns a Lazy object, which behaves almost like an Array, except that it executes the methods in a pipeline fashion and dynamically stops when no more elements are required, without creating any temporary arrays in memory:

for line in (DATA.lazy.grep{ _ < 'd' }.map{ print ">> "; .uc }) {
   print line
}

__DATA__
a
b
c
d

Output (which shows that .map{} is really lazy):

>> A
>> B
>> C

This mechanism is generic enough to support any kind of object that implements the .iter() method. Currently, it includes the following built-in types: Array, FileHandle, DirHandle, RangeNumber and Lazy.

Lazy methods

Lazy methods provide an interesting concept of partially applying a method on a given object, delaying the evaluation until the result is needed.

var lz = "foo".method('>')        # LazyMethod
say lz("bar")                     # true

Object.method() returns a LazyMethod object which can be called just like any function, producing the result by calling the method which is stored inside the LazyMethod object.

Functions

Like mathematical functions, Sidef's functions can be recursive, take arguments and return values.

func hello (name) {
   say "Hello, #{name}!"
}

hello("Sidef")

Recursive functions:

func factorial (n) {
   if (n > 1) {
       return (n * factorial(n - 1))
   }
   return 1
}

say factorial(5);     # prints: 120

Anonymous functions:

func recmap(repeat, seed, transform, callback) {
    func (repeat, seed) {
        callback(seed)
        repeat > 0 && __FUNC__(repeat-1, transform(seed))
    }(repeat, seed)
}

recmap(6, "0", func(s) { s + s.tr('01', '10') }, func(s) { say s })

Storing functions in variables:

var f = func (name) {
     say "#{name} says 'Hello!'"
}
f('Sidef')

Function re-declaration:

func f(name) {
   say "Hello, #{name}"
}
f("Sidef")

f = func (name, age) {
   say "Hello, #{name}! You claim to be #{age} years old."
}
f("Sidef", 100)

Closures

In Sidef, all functions are first-class objects which can be passed around like any other object. Additionally, all functions (including methods) are lexical closures.

func curry(f, *args1) {
    func (*args2) {
        f(args1..., args2...)
    }
}

func add(a, b) {
    a + b
}

var adder = curry(add, 1)
say adder(3)                   #=>4

Automatically cached functions

By specifying the cached trait to a function or a method, Sidef will automatically cache it for you.

func fib(n) is cached {
    return n if (n <= 1)
    fib(n-1) + fib(n-2)
}
say fib(100)              # prints: 354224848179261915075

Function parameters

The parameters of a function can be defined to have a default value when the function is called with a lower number of arguments than required.

func hello (name="Sidef") {
    say "Hello, #{name}!"
}

hello()           # prints: "Hello, Sidef!"
hello("World")    # prints: "Hello, World!"

The default value of a parameter is evaluated only when an argument is not provided for that particular parameter, and it can be any expression you like:

func foo (a = 1.25.sqrt, b = 1/2) {
     a + b
}

say foo()           # prints the result of: sqrt(1.25) + 1/2
say foo(21, 21)     # prints: 42

An interesting feature is the possibility of referring to a previously defined parameter as a default value:

func foo(a=10, b=a+1) {
    a + b
}

say foo()              # prints: 21 (the result of: 10 + 10+1)
say foo(1)             # prints: 3  (the result of: 1 + 1+1)
say foo(21,21)         # prints: 42 (the result of: 21 + 21)

Named parameters

This is a very nice feature borrowed from Perl 6 which allows a function to be called with named parameters, giving us the flexibility to put the arguments in no specific order:

func div(a, b) {
   a / b
}

say div(b: 5, a: 35)  # prints: 7

Variadic functions

A slurpy variable in the form of *name can be used as a function parameter to collect the remaining arguments inside an array:

func f(*args) {
   say args  #=> [1, 2, 3]
}

f(1, 2, 3)

Alternatively, by using a named variable in the form of :name, the arguments are collected inside an hash:

func f(:pairs) {
   say pairs         # prints: Hash(a => 1, b => 2)
}

f(a => 1, b => 2)

Typed parameters

A function can be declared with typed parameters, which are checked at run-time.

func concat(String a, String b) {
    a + b
}

Now, the function can only be called with strings as arguments:

say concat("x", "y")  # ok
say concat(1, 2)      # run-time error

The typed parameters require a specific type of object, but they do not default to anything when no value is provided.

This means that all the typed-parameters are mandatory, unless a default value is provided:

func concat(String a="foo", String b="bar") {
   a + b
}

say concat()          # prints: "foobar"
say concat("mini")    # prints: "minibar"
say concat(1, 2)      # this is still a run-time error

Subsets

A subset is a definition which specifies the upper limit of inheritance, with optional argument validation.

subset Integer     < Number  { |n| n.is_int }
subset Natural     < Integer { |n| n.is_pos }
subset EvenNatural < Natural { |n| n.is_even }

func foo(n < EvenNatural) {
    say n
}

foo(42)         # ok
foo(43)         # failed assertion at run-time

In some sense, a subset is the opposite of a type. For example, let's consider the following class hierarchy:

class Hello(name) {
    method greet { say "Hello, #{self.name}!" }
}

class Hi < Hello {
    method greet { say "Hi, #{self.name}!" }
}

class Hey < Hi {
    method greet { say "Hey, #{self.name}!" }
}

If we declare a function that accepts a subset of Hi, it will accept Hello, but it cannot accept Hey:

func greet(obj < Hi) { obj.greet }       # `Hi` is the upper limit

greet(Hi("Foo"))        # ok
greet(Hello("Bar"))     # ok
greet(Hey("Baz"))       # fail: `Hey` is too evolved

On the other hand, if we use Hi as a type assertion, it will accept Hey, but not Hello:

func greet(Hi obj) { obj.greet }        # `Hi` is the lower limit

greet(Hi("Foo"))        # ok
greet(Hey("Baz"))       # ok
greet(Hello("Bar"))     # fail: `Hello` is too primitive

Multiple dispatch

Starting with version 2.12, Sidef includes multiple dispatch for functions and methods, based on the number of arguments and their types.

func test(String a){
    say "Got a string: #{a}";
}

func test(Number n) {
    say "Got a number: #{n}";
}

func test(Number n, Array m) {
    say "Got a number: #{n} and an array: #{m.dump}";
}

func test(String s, Number p) {
    say "Got a string: #{s} and a number: #{p}";
}

test("hello", 21);
test("sidef");
test(12, [1,1]);
test(42);

Output:

Got a string: hello and a number: 21
Got a string: sidef
Got a number: 12 and an array: [1, 1]
Got a number: 42

Functional pattern matching

This is one of the newest and nicest features of Sidef and it's available starting with version 2.12.

The feature looks like this:

func fib ((0)) { 0 }
func fib ((1)) { 1 }
func fib  (n)  { fib(n-1) + fib(n-2) }

say fib(12)      # prints: 144

We have three functions, where the first two, each have an expression as a parameter. Sidef will check each expression for equality with a given argument and will call the corresponding function when it passes the test. Otherwise, it will default to the third function.

Additionally, we can specify blocks instead of expressions:

func fib (Number n { _ <= 1} = 0) {
    return n
}

func fib (Number n) {
    fib(n-1) + fib(n-2)
}

say fib(12)      # prints: 144

When a block is specified, the type and the name of the variable must come before the block and any default values goes after the block. The types and the default values can be omitted.

Also, we can omit the name of the parameters as well:

func fib({.is_neg})  { NaN }
func fib({.is_zero}) { 0 }
func fib({.is_one})  { 1 }
func fib(n)          { fib(n-1) + fib(n-2) }

say fib(12)      # prints: 144

Returned type

Sidef also has the capability to check the return type of a function and stop the execution of the program if the returned type doesn't match the type defined in the function.

func ieq(a, b) -> Bool {
   a.lc == b.lc
}

say ieq("Test", "tEsT")    # true

On the other hand:

func concat(a, b) -> Array {
    a + b
}

say concat([1,2,3], [4,5,6])   # ok
say concat("123", "456")       # run-time error

Multiple return-type checks are supported as well:

func foo() -> (Number, String) {
   (42, "foo")
}
var (a, b) = foo()

Alternative function declaration

A function can also be declared by using the fancy unary operator ->, which is synonym with the func or method keywords, depending on the context where it is used.

-> my_add(a,b) { a + b }

If you enclose that function inside a class, it will be declared as a method belonging to that class. However, if you enclose it inside another method, it will still be declared as a private function to that method.

The name of the function is optional. If the name is omitted, an anonymous function will be created.

[1,2,3].sort(->(a,b) { b <=> a })

Loops

Creating loops in Sidef is as simple as it can get. Any loop can be stopped with the break keyword.

Infinite looping with the loop keyword:

loop {
  say "Sidef is looping!"
}

while loop:

var x = 123456789
while (x > 0) {
   var (div, mod) = x.divmod(10)
   say mod
   x = div
}

do-while loop:

var n = 1
do {
   say n
} while (++n <= 10)

for loop:

for (var i = 0; i <= 10; i++) {
   say i
}

for-in loop:

for item in (1..5) {
    say item
}

foreach loop (deprecated):

var arr = 1..5
for (arr) { |item|
    say item
}

Array each method:

var arr = 1..5
arr.each { |i|
    say i**2
}

Repetition block:

{ |n|
  say "Hello there! (#{n})"
} * 10

or:

n.times { ... }

Recursive block:

{
  say "Hello, World!"
  __BLOCK__.run
}.run

Things to remember:

  1. run is used for running a block.
  2. call is used for calling a block.
  3. .each{} method is used for iteration over arrays.
  4. while is used to run the block as long as the condition is true.
  5. |x,y,z| is used to capture the block arguments in variables.
  6. The keyword break will break a loop.
  7. Any block is an object and can be stored in variables and executed at any time.

Strings

"I am a string"

A string is a group of characters which exists inside the same object.

var new_str = ("a" + "b")
say new_str   # prints: "ab"

A Sidef-string have many methods which will really help working with strings in a new exciting way.

Most used methods are:

.uc                     # uppercase the string
.lc                     # lowercase the string
.wc                     # capitalize each word
.tc                     # capitalize the string
.reverse                # reverse the string
.trim                   # removes leading and trailing whitespace

.length                 # string size, in characters
.is_empty               # true if string has 0 length

.div(N)                 # divide the string into N chunks
.split(N)               # split the string by N characters
.split('str')           # split the string by 'str'
.split(/regex/)         # split the string by a regular expression
.each{|c| ...}          # iterate over string characters
.each_word{|w| ...}     # iterate over words
.each_line{|l| ...}     # iterate over lines

.char(3)                # returns the character at the specified index
.substr(beg, len)       # returns a sub-string from position beg
.begins_with('str')     # true if string begins with 'str'
.ends_with('str')       # true if string ends with 'str'
.contains('str')        # returns true if string contains 'str'
.index('str')           # returns the position where 'str' begins
.match(/regex/)         # returns a Match object

.sub(/regex/, 'str')    # regexp substitution (returns a new string)
.gsub(/regex/, 'str')   # global regexp substitution (returns a new string)

For more methods, see: String.pod

String quotes

Being a new programming language, Sidef has built-in support for Unicode quotes.

var dstr = „double quoted”
var sstr = ‚single quoted’

The difference between double-quoted and single-quoted strings is the following:

  1. double-quoted strings can interpolate code.
    • example: "code: #{say 'inside string'}"
  2. double-quoted strings understand special escapes.
    • example: "\n, \t, \Q...\E"

while the single-quoted strings can't do any of this.

say 'single\tquoted'   # prints the string as it is
say "double\tquoted"   # replaces '\t' with a tab-character

var name = "string";
say 'single quoted #{name}'     # prints the string as it is
say "double quoted #{name}"     # prints: "double quoted string"

A single word can also be quoted by placing : in front of it. This feature makes things easier when it's used in hash look-ups and hash literal definitions.

:word == 'word'
:another_word == 'another_word'

Sidef also borrow from Ruby some operator-like quotes and implements some new ones:

var sstr = %q{single {} quoted string}
var dstr = %Q«double «» quoted string»
var arr1 = %w(word1 word2)
var arr2 = %W(double quoted words)
var arr3 = <single <quoted> words>
var arr4 = «double «quoted» words»
var reg  = %r「some\h+regxp?」
var file = %f„filename.txt”
var dir  = %d‘/my/dir’

Simple HERE-doc support:

var str = <<'EOF'
some
text
EOF

Indented HERE-document (with interpolation):

func hello(arg) {
    return <<"-EOT"
    Hello, #{arg}!
    EOT
}

print hello('Sidef')    # prints: 'Hello, Sidef!'

You can also single-quote an indented HERE-document, which will not interpolate:

{
    print <<'-EOT';
    Not interpolated: #{1+2}
    EOT
}.run;     # prints: 'Not interpolated: #{1+2}'

Multiple HERE-docs can, also, be combined:

print(<<'EOF', "- - -\n", <<'EOT' + <<"EOD");
1 2 3
EOF
4 5 6
EOT
7 8 9
EOD

Numbers

In Sidef, numbers play an important role and are treated correspondingly. Starting with version 2.20, the numerical system is implemented with Math::GMPq, Math::GMPz, Math::MPFR, and Math::MPC, giving us a really good precision in calculations with an astonishing performance.

say Number.pi       # prints: 3.1415926535897932384626433832795
say 2**999          # 535754303593133660474212524530...0214915826312193418602834034688

Integers:

Here is 255 written as integer in different bases:

255;              # decimal
0xff;             # hexadecimal
0377;             # octal
0b1111_1111;      # binary

Float

In Sidef, there is no real distinction between integers and floating-point numbers, as all numbers are represented by Math::GMPq in rational form.

1.234;          # 1.234
.1234;          # 0.1234
1234e-5;        # 0.01234
12.34e5;        # 1234000

Arrays

var array = [1, 2, 3, 4, 5]

array[0] = 6
array[1] = 7

say array

Arrays are simple objects which can store other objects. In Sidef, an array can grow as long as the system memory permits it.

Array autovivification:

var array = []
array[3][4] = "hei"
say array

If you're familiar with Perl, you already know about autovivification. It's the feature responsible for the dynamic creation of data structures.

Array filtering

The Array object has many interesting methods for making it safer and easier to work with arrays in a pure OO style.

greping some elements from an array:

var new_arr = arr.grep { _ > 10 }      # returns a new array containing numbers greater than 10

maping an array:

var new_arr = arr.map {|n| 2*n + n**2}   # returns a new array with the result returned from the "map" block

sorting an array:

# generic sort
var new_arr = arr.sort

# naive string case-insensitive sort
var new_arr arr.sort {|a,b| a.lc <=> b.lc}

# efficient string case-insensitive sort
var new_arr arr.sort_by { .lc }

Array unrolling

It's a nice feature from Perl 6 which unrolls two arrays and applies the operator to each argument[i], creating a new array with the results. The operator can be a method or any other valid operator and must be enclosed between » « or >> <<.

[1,2,3] »+« [4,5,6]           # [1+4, 2+5, 3+6]
%w(a b c) >>cmp<< %w(c b a)   # [-1, 0, 1]

Internally, the unroll_operator method is called, which can, also, be implemented in user-defined classes.

Array mapping

The array mapping operator works exactly like the Array.map{} method, but it's much efficient and easier to write. The mapping operator must be enclosed between » » or >> >>.

[1,2,3] »*» 4      # [1*4, 2*4, 3*4]

Internally, the map_operator method is called.

Array reverse mapping

The reverse mapping calls the operator on the specified argument and each element of the array becomes an argument to the operator. The reverse mapping operator must be enclosed between « « or << <<.

[1,2,3] «/« 10     # [10/1, 10/2, 10/3]

Internally, the pam_operator method is called.

Array reducing

This hyper-operator reduces an array to a single element. The operator needs to be enclosed inside « » or << >>.

[1,2,3+»      # 1 + 2 + 3
[1,2,3/»      # 1 / 2 / 3

Internally, the reduce_operator method is called.

Array crossing

The metaoperator ~X or ~Xop crosses two arrays and returns a new one.

[1,2] ~X+ [3,4]    # [1+3, 1+4, 2+3, 2+4]
[1,2] ~X  [3,4]    # [[1,3], [1,4], [2,3], [2,4]]

Internally, the cross_operator method is called.

Array zipping

The metaoperator ~Z or ~Zop zips two arrays and returns a new one.

[1,2] ~Z+ [3,4]    # [1+3, 2+4]
[1,2] ~Z  [3,4]    # [[1,3], [2,4]]

Internally, the zip_operator method is called.

Matrix iteration

The extended for-in loop, introduced in Sidef 2.23, has built-in support for matrix iteration, which makes it useful to combine it with the cross or zip array hyper-operators.

for a,b in ([1,2] ~X [3,4]) {
    say "#{a} #{b}"
}

Output:

1 3
1 4
2 3
2 4

Lists

An array can be converted into a list using the following notations:

var arr = ["a", 1, "b", 2]
say Hash(arr...)             # creates an Hash by passing the array as a list of values
say Hash(@|arr)              # ==//==

The difference between postfix ... and prefix @| consists in the fact that @| invokes the ... method only when its argument can respond to this method, while in the first case, the ... method is invoked unconditionally.

Slices

A slice is a sub-array, just like a sub-string is for a string.

var arr = ["foo", "bar", "baz"]

var *slice1 = arr[0, 1]         # fetches the first two values

var indices = [-2, -1]
var *slice2 = arr[indices]      # automatically unpacks the `indices` and fetches the last two values

say slice1                      # prints: ["foo", "bar"]
say slice2                      # prints: ["bar", "baz"]

Using slices, it's also possible to change multiple values inside an array:

var arr = ["foo", "bar", "baz"]
arr[0, 1] = ("a", "b")          # changes the first two values
say arr                         # prints: ["a", "b", "baz"]

Alternatively, using indices stored inside an array:

var arr = ["foo", "bar", "baz"]
var indices = [0, 1]
arr[indices] = ("a", "b")       # changes the first two values
say arr                         # prints: ["a", "b", "baz"]

An empty [], will return the entire array as a list of lvalues:

var arr = ['a', 'b', 'c']
arr[] = ('foo', 'bar')         # replaces the entire array
say arr                        # prints: ['foo', 'bar']

Multi-arrays

A multi-array is an object which incorporates one or more arrays of the same length.

var multi_arr = [1,3]:[2,4]

Now, the multi_arr contains two arrays. We can iterate over each pair, using the each method:

multi_arr.each { |x,y|
    say "#{x} #{y}";
}

which outputs:

1 2
3 4

To combine more than two arrays, we need to use one of the following methods:

MultiArr([1, 2, 3],
         [4, 5, 6],
         [7, 8, 9])

or

[1,2,3].pair_with([4,5,6],[7,8,9])

Ranges

A range is a definition of some consecutive values, either in increasing or decreasing order. The main advantage of a range over an array is that a range can be infinite, while an array can't, and this is because ranges are lazy.

A range can be created with one of the following operators: .., ^.. or ..^;

Mnemonics:

  • .. go from left to right (inclusive)
  • ..^ begin from down and go up (exclusive)
  • ^.. begin from up (exclusive) and go down

Count from 1 to 10:

for i in (1 .. 10) {
   say i
}

Count from 1 to 9:

for i in (1 ..^ 10) {
   say i
}

Count from 9 to 1:

for i in (10 ^.. 1) {
   say i
}

A range can be shifted (+, -), stretched (*) and shrank (/):

(1..10) + 2       #=>   3 .. 12
(1..10) - 2       #=>  -1 ..  8
(1..10) * 2       #=>   2 .. 20
(1..10) / 2       #=> 0.5 ..  5

Also, ranges can be reversed (.reverse) and granularized (.by):

(1..10).reverse        # a new reversed range from 10 down to 1
(1..10).by(0.5)        # a range from 1 up to 10, counting by 0.5

Alternatively, ranges can be created with the RangeNum data-type:

var evens = RangeNum(0, Inf, 2)      # range of all even numbers
say evens.lazy.first(10)             # the first 10 even numbers

A negative third argument will create a descending range:

for i in RangeNum(10, 5, -1) {    # count from 10 down to 5
    say i
}

Hashes

Hashes are used to quickly locate a data record (e.g., a dictionary definition) given its search key.

var hash = Hash(
    name => 'foo',
    age  => 42,
)

Working with hashes is almost the same as working with arrays, but instead of specifying a position index in square brackets, we now lookup with a string specified in curly brackets.

say hash{:name}    # fetches the value for 'name'

Changing a hash value:

hash{"age"} = 99

Multiple values

Just like arrays, hashes also support the retrieving of multiple values at once.

var hash = Hash(
    a => 1,
    b => 2,
    c => 3,
)

# Returns a list of values
var *vals = hash{:a, :b, :c}

# Print the values
say vals                        # [1,2,3]

# Using the keys defined inside an array
var keys = %w(a b c)
var *vals = hash{keys}

# Print the values
say vals                        # [1,2,3]

An empty {} will return the entire hash as a list of lvalue-pairs:

var hash = Hash(a => 1, b => 2)
hash{} = (c => 3, d => 4)         # replaces the entire hash
say hash                          # prints: Hash(c => 3, d => 4)

Working with hashes

Hashes, like everything else, are objects which have many methods built-in, helping in dealing with hash tables.

Sorting by value:

# Sorting in ascending order
var asc_array = hash.sort_by{|_,v| v }

# Sorting in descending order
var des_array = hash.sort_by{|_,v| v }.reverse

For sorting the keys based on values, you can use traditional key-sorting:

# Sorting in ascending order
var keys_array = hash.keys.sort{|a,b| hash{a} <=> hash{b}}

# Sorting in descending order
var keys_array = hash.keys.sort{|a,b| hash{b} <=> hash{a}}

For more methods, see: Hash.pod

Pairs

Creating a long chained list in Sidef is as simple as it can get:

var tree = 'root':'child':'grandchild':'end'

The above code creates a pair of pairs, which looks like this:

Pair('root',
    Pair('child',
        Pair('grandchild', 'end')
    )
)

For traversing the list, we can use:

loop {
    say tree.first
    tree.second.is_a(Pair) || break
    tree = tree.second
}

This is also useful in creating an array of pairs:

var array_of_pairs = [
    "red":   9,
    "blue":  4,
    "green": 0,
]

Now, each element of the array is a Pair and can be accessed by using the first and second methods:

array_of_pairs.each { |pair|
    say "#{pair.first} == #{pair.second}"
}

Structs

A structure is very similar with a class without methods and can be used as a stricter alternative to hashes.

struct Person {
    String name,
    Number age,
}

# Create a new person
var john = Person(name: "John Smith", age: 42);

# Change a value
john.name = "Dr. #{john.name}";

# Increment a value
john.age++;

say john.name;       #=> Dr. John Smith
say john.age;        #=> 43

Files

A File is a built-in type in Sidef, in the same way as a String or an Array is.

Declaring a File object:

var file1 = File('/tmp/abc.txt')
var file2 = %f(/tmp/abc.txt)            # same thing

Being an object, it can have some interesting methods, and it does. The following code will simply edit a file in place:

File('/tmp/abc.txt').edit { |line|
    line.gsub(/this/, 'that')               # replaces 'this' with 'that' anywhere inside the file
}

File info

Here is a list with the most important methods which verifies some attributes of the file.

file.exists;       # true if file exists
file.size;         # size of the file
file.is_emtpy;     # true if size is zero
file.is_dir;       # true if file is a directory
file.is_readable;  # true if file is readable
file.is_link;      # true if file is a link
file.is_text;      # true if file is a text-file

Some more information can be achieved by using the stat or lstat method:

var info = file.stat;    # or 'lstat'
say info.atime;
say info.mtime;
say info.ctime;
say info.size;

Information related to the self object file:

file.name;        # the original name of the file
file.base;        # the base name of the file
file.abs;         # the absolute path the file
file.dir;         # the parent directory of the file

Manipulating files

We can also delete and rename files and do other things to files.

file.rename("new-name.ext");       # rename file
file.move("new-name.ext");         # rename file safer
file.copy("new-name.ext");         # copy file
file.unlink;                       # delete file
file.touch;                        # create file if it doesn't exists
file.chmod(0666);                  # change the permissions
file.utime(atime, mtime);          # change the access and modification times

Open files

A File object has a main open method which is directly bound to Perl's open function.

file.open('<:utf8', \var fh, \var err) ->
     || die "Can't open file #{file}: #{err}\n"

A simpler interface is provided by the open_* methods:

var fh   = file.open_r              # open the file for reading
var bool = file.open_r(\var fh)     # same thing, but returns a Boolean value

File handles

To read the content of a file inside a string, we can use the .slurp method on a FileHandle object:

var content = fh.slurp             # reads the content of the file and returns the result

To read one line at a time, we can use the FileHandle.each {} method:

fh.each { |line|
     say "[#{$.}] #{line.chomp}"
}

Conditional expressions

Conditional expression are almost the same in most programming languages and Sidef will not make an exception, so we'll have the classic if, while and for conditional expressions.

if (bool) {

}
elsif (bool) {

}
else {

}
while (bool) {

}
for (var i = 0; i <= 10; i++) {

}

Also, we have the ternary operator and the case and switch statements:

bool ? (true) : (false)

Given/when is used to compare two values using the rules of the smartmatch operator (~~):

given ('b') {
  when ('a') { say "a" }
  when ('b') { say "b" }
  default    { say "Unknown!" }
}

Additionally, to test expressions for trueness, Sidef has the case statement:

given (-1) {
  case (.is_zero) {
       say "Null value!"
  }
  case (.is_neg) {
       say "Negative value!"
  }
  default {
       say "Positive value!"
  }
}

case and when statements can be mixed together:

given (42) { |value|
  case (value < 0) {
       say "Negative value!"
  }
  when (0) {
       say "Null value!"
  }
  case (value > 1) {
       say "Positive value!"
  }
}

When a value is found, the construct breaks automatically, but we have the continue keyword which will prevent this.

given (1) {
   when (1) {
      say "true once"
      continue        # will fall through
   }
   when (1) {
      say "true twice"
   }
}

Regular expressions

Sidef borrows the regular expressions from Perl. Any regular expression is analyzed and compiled by Perl's regexp engine, so we have all the good stuff we are already familiar with.

var regex = /^my+[regex]?\z/ixmsu

Matching against regular expressions:

var string = 'something'

if (string =~ regex) {
   say "Matches!"
}

Storing and using the captured matches:

var string = "Sidef <3 Perl"
var match = string.match(/(\w+)\h+<3\h+(\w+)/)

if (match) {
   var captures = match.captures
   say captures[0]                 # prints: Sidef
   say captures[1]                 # prints: Perl
}

The returned match object, it's a special object which accepts array indexing of values.

var m = "hello world".match(/^(\w+) (\w+)/)
say m[0]                                   # prints: hello
say m[1]                                   # prints: world

Global matching

A regex can match multiple times inside a given string, therefore Sidef provides support for global matching.

var str = "a cat, a dog and a fox"
while (var m = str.match(/\ba\h+(\w+)/g)) {
    say m[0]
}

Alternatively, there is the String.gmatch() method:

var string = 'Sidef <3 regular expressions'
while (var m = string.gmatch(/(\S+)/)) {
    say m[0]
}

Smart-matching

The smart-match operator (~~) take two operands and compare them based on their type and their order.

"hello" ~~ /^h/;        # true: string matches regex
"oo" ~~ "foobar";       # false: "oo" doesn't equal "foobar"
"a" ~~ %w(a b c);       # true: item exists in array
/^b/ ~~ %w(foo bar);    # true: regex matches an element from array
/^f/ ~~ Hash(foo => 1); # true: regex matches a key from hash

There is also !~ which simply flips the Boolean value returned by ~~.

/abc/ !~ "abcdef";     # false

Modules

A module in Sidef is an independent piece of code with its own namespace and variables.

module Fibonacci {
    func nth(n) {
        n > 1 ? nth(n-2)+nth(n-1) : n
    }
}

say Fibonacci::nth(12)    # prints: 144

A module is independent in the way that you can't access variables from other namespaces:

var x = 42
module Test {
   say x        # parse-time error: can't find variable 'x'
}

Classes

A class is just a distinct group of functions, which we now call them methods, and a bunch of instance-variables. When a class is called, an instance-object of that class is returned, which encapsulates the given data provided at class initialization. Each call to a class, returns a different instance-object.

class Person (name, age, address) {
    method position {
        # GPS.locate(self.address)
    }

    method increment_age(amount=1) {
         self.age += amount
    }
}

var obj = Person(name: "Foo", age: 50, address: "St. Bar")
say obj.age                # prints: 50
say obj.name               # prints: "Foo"
say obj.address            # prints: "St. Bar"

obj.name = "Baz"           # changes name to "Baz"
say obj.name               # prints: "Baz"

obj.increment_age          # increments age by 1
say obj.age                # prints: 51

Classes in Sidef are a little bit different than traditional classes. Values of a class are accessible via a method call which can either get or set a value, so the programmer don't have to worry about creating separated get_* and set_* methods.

Class inheritance:

A class can inherit methods from other classes by using the special << operator-like, followed by one or more class names separated by commas:

class Animal {

}

class Mammal << Animal {

}

class Reptile << Animal {

}

class Dog << Mammal {

}

A more detailed example:

class Phone {
    method turnOn {
        say "Turned ON"
    }
}

class Samsumg(String model) < Phone {
    method getWIFIConnection() {
        say "WIFI connected"
    }

    method cameraClick() {
        say "Camera clicked"
    }

    method cameraClick(String cameraMode) {
        say ("Camera clicked in " + cameraMode + " Mode")
    }
}

var s6 = Samsumg(model: "Galaxy S6 Edge")

s6.turnOn()
s6.cameraClick()
s6.cameraClick("Panorama")

Output:

Turned ON
Camera clicked
Camera clicked in Panorama Mode

Class fields inheritance

An experimental feature, introduced in version 2.24, allows the inheritance of class variables and class attributes from inherited classes:

class Foo(x = "a") {

}

class Bar(y = 1) < Foo {

}

class Baz(z) < Bar {

}

var obj = Baz(y: 42, z: 'b')

say obj.x       # => 'a'
say obj.y       # => 42
say obj.z       # => 'b'

Metaprogramming

An interesting feature is the definition of methods at run-time:

var colors = Hash(
               'black'   => "000",
               'red'     => "f00",
               'green'   => "0f0",
               'yellow'  => "ff0",
               'blue'    => "00f",
               'magenta' => "f0f",
               'cyan'    => "0ff",
               'white'   => "fff",
             )

for color,code in colors {
    String.def_method("in_#{color}", func (self) {
        '<span style="color: #' + code + '">' + self + '</span>'
    })
}

say "blue".in_blue
say "red".in_red
say "white".in_white

Output:

<span style="color: #00f">blue</span>
<span style="color: #f00">red</span>
<span style="color: #fff">white</span>

Methods can have variable-like names (a, hello, etc...), or operator-like names (+, **, etc...).

class Number {
    method ⊕(arg) {
        self + arg
    }
}

say (2142)

The special method AUTOLOAD is called when a given method is missing.

class Example {
    method foo {
        say "this is foo"
    }
    method bar {
        say "this is bar"
    }
    method AUTOLOAD(_, name, *args) {
        say ("tried to handle unknown method %s" % name);
        if (args.len > 0) {
            say ("it had arguments: %s" % args.join(', '));
        }
    }
}

var example = Example()

example.foo           # prints “this is foo”
example.bar           # prints “this is bar”
example.grill         # prints “tried to handle unknown method grill”
example.ding("dong")  # prints “tried to handle unknown method ding”
                      # prints “it had arguments: dong”

Parallel computation

Sidef has a very basic support for parallel computation, but pretty powerful. There is the Block.ffork() method which creates a new system process that executes (in parallel) the content of a given block and returns a new Fork object which accepts the wait (or get) method that waits for the process to finish and returns the computed value.

To take advantage of this mechanism, fork two or more processes and store the returned objects in variables or inside an array and get their values at a later time. The process is executed soon after it has been forked.

Example for the quicksort algorithm in parallel:

func quicksort(arr {.len <= 1}) { arr }

func quicksort(arr) {

    say arr;
    var p = arr.pop_rand;

    var forks = [
        quicksort.ffork(arr.grep { _ <= p }),
        quicksort.ffork(arr.grep { _ >  p }),
    ];

    forks[0].wait + [p] + forks[1].wait
}

say quicksort(@("a".."z") -> shuffle)

Alternatively, Sidef provides the Block.thr method, which creates a deprecated Perl thread, or a system fork if forks is installed.

Interacting with Perl modules

Sidef can interact with Perl modules in a very easy way. There is the require keyword which will try to load an object-oriented Perl module.

var lwp  = require('LWP::UserAgent')
var ua   = lwp.new(show_progress => 1)
var resp = ua.get('http://example.com')

if (resp.is_success) {
    say resp.decoded_content.length
}

For functional Perl modules, the frequire keyword should be used instead to denote that the module is function-oriented.

var spec = frequire('File::Spec::Functions')
say spec.rel2abs(spec.curdir)

Literal Perl modules

There is a special syntax for literal Perl modules, which creates a special module-caller object, without require-ing the module in the first place.

%s<LWP::UserAgent>;         # object-oriented module
%S<File::Spec::Functions>;  # function oriented module

This allows us to ignore the return value of require and declare the module-caller afterwards.

require('ntheory')

var nt = %S<ntheory>

if (nt.is_prime(43)) {
    say "43 is prime!";
}

nt.forprimes({|p| say p }, 0, 100)

Graphical interface

Due to the fact that we can interact with Perl modules, we can also create graphical interfaces from Sidef, by using the Gtk2 library.

require('Gtk2') -> init

var gtk2   = %s<Gtk2>
var window = %s<Gtk2::Window>.new
var label  = %s<Gtk2::Label>.new('Goodbye, World!')

window.set_title('Goodbye, World!')
window.signal_connect(destroy => { gtk2.main_quit })

window.add(label)
window.show_all

gtk2.main

Inlining Perl code in Sidef

Perl.eval() can evaluate arbitrary Perl code and convert the result into a Sidef data structure which can be used later in your program.

var perl_code = <<'CODE';

sub fact {
    my $p = 1;
    $p *= $_ for 2..$_[0];
    $p;
}

my %data = (
    result => fact(10)
);

\%data;
CODE

var data = Perl.eval(perl_code);
say data{:result};                 #=> 3628800
say Sys.ref(data{:result});        #=> Sidef::Types::Number::Number

A practical example would be the creation of Sidef blocks which incorporate arbitrary Perl code. Doing so, the returned object will behave exactly like a native Sidef block:

var perl_code = <<'CODE';

Sidef::Types::Block::Block->new(
    code => sub {
        my ($n) = @_;
        ($n <= 1) ? $n
                  : (CORE::__SUB__->($n-1) + CORE::__SUB__->($n-2));
    },
    type => 'func',
    name => 'fib',
    table => {'n' => 0},
    vars => [{name => 'n'}],
);

CODE

var block = Perl.eval(perl_code)
say block(28)