Skip to content

Latest commit

 

History

History
2174 lines (1579 loc) · 45.1 KB

File metadata and controls

2174 lines (1579 loc) · 45.1 KB

Sidef Programming Language — Beginner's Guide

A modern, high-level programming language with beautiful syntax and powerful math

Try Online (no install needed)Official DocumentationGitHub


Table of Contents

  1. What is Sidef?
  2. Installation
  3. Your First Program
  4. How to Run Sidef Code
  5. Comments
  6. Variables
  7. Numbers
  8. Strings
  9. Booleans
  10. Operators and Expressions
  11. Operator Precedence — The Most Important Rule
  12. Printing Output
  13. Getting Input
  14. Control Flow — Making Decisions
  15. Loops
  16. Functions
  17. Arrays
  18. Hashes (Dictionaries)
  19. String Operations
  20. Working with Files
  21. Error Handling
  22. A Taste of Sidef's Math Powers
  23. Beginner Projects to Try
  24. Where to Learn More

1. What is Sidef?

Sidef is a modern, high-level programming language that runs on top of Perl. It takes inspiration from several languages:

  • Ruby — clean, readable syntax
  • Raku — expressive features and metaoperators
  • Julia — powerful mathematical capabilities

Why learn Sidef?

  • Exact numbers by default. Sidef stores decimals as exact fractions, so 0.1 + 0.2 == 0.3 is actually true — unlike most other languages.
  • Giant numbers, no effort. It handles arbitrarily large integers and floats without any special libraries.
  • Rich built-in math. Prime numbers, factorization, number theory — all built in.
  • Clean, readable syntax. Code reads almost like plain English.
  • Functional and object-oriented. You can mix styles naturally.

A quick taste

# Print the first 10 prime numbers
say 10.primes

# Exact decimal math (no floating-point surprises!)
say (0.1 + 0.2 == 0.3)    # true

# Factorial of 100 — a 158-digit number!
say 100!

# Sum all numbers from 1 to 100
say (1..100).sum

2. Installation

💡 Want to try Sidef without installing anything? Head to https://tio.run/#sidef and run code right in your browser.

Sidef requires three C math libraries: GMP, MPFR, and MPC. These provide big-number support. Your package manager handles them automatically with the commands below.


Linux (Debian, Ubuntu, Linux Mint, and derivatives)

sudo apt install libgmp-dev libmpfr-dev libmpc-dev libc-dev cpanminus
cpanm --sudo -n Sidef

Then verify:

sidef -v

Linux (Arch Linux / Manjaro)

trizen -S sidef

macOS (with Homebrew)

brew install gmp mpfr libmpc
cpan Sidef

Android (Termux)

pkg install perl make clang libgmp libmpfr libmpc
cpan -T Sidef

Windows

Download the ready-to-run executable from the GitHub Releases page. Unzip and run sidef.exe from the command prompt.


Installing via CPAN (any platform)

If you have Perl installed, you can always install via CPAN:

cpan Sidef
# Or skip the test suite for a faster install:
cpan -T Sidef

Building from source

wget 'https://github.com/trizen/sidef/archive/master.zip' -O master.zip
unzip master.zip
cd sidef-master
perl Build.PL
sudo ./Build installdeps
sudo ./Build install

Verify your installation

sidef -v    # Print version
sidef -h    # Print help

3. Your First Program

Create a file called hello.sf (Sidef files use the .sf extension):

#!/usr/bin/sidef

say "Hello, World!"

The first line (#!/usr/bin/sidef) is called a shebang — it's optional, but it tells the operating system which program to use to run this file. You can leave it out if you want.

Save the file and run it (see the next section for how to do that). You should see:

Hello, World!

Congratulations — you just ran your first Sidef program! 🎉


4. How to Run Sidef Code

Run a script file

sidef hello.sf

Run code directly from the command line (one-liners)

Use the -e flag for quick experiments:

sidef -e 'say "Hello!"'
sidef -e 'say (2 ** 10)'
sidef -e 'say 5.primes'

The interactive REPL

Run sidef with no arguments to enter the interactive prompt, where you type expressions and see results immediately:

$ sidef
Sidef 26.04, running on Linux, using Perl v5.42.1.
Type "help", "copyright" or "license" for more information.
> say "Hello!"
Hello!
> (2 + 3)
5
> 10.primes
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
> 100!
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

The REPL is great for exploring the language and testing ideas. Press Ctrl+D or type exit to quit.

Try it online

If you don't want to install anything, use the online playground: https://tio.run/#sidef


5. Comments

Comments are notes in your code that Sidef ignores when running. They're for humans, not computers.

# This is a single-line comment.
say "Hello!"    # Comments can go at the end of a line too.

/*
   This is a multi-line comment.
   It can span as many lines as you need.
   Useful for longer explanations.
*/
say "After the comment block."

💡 Good habit: Write comments to explain why your code does something, not just what it does. Your future self will thank you.


6. Variables

Variables store values that your program can use and manipulate. The standard way to declare a variable in Sidef is with the var keyword:

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

Variable Types

Sidef has four kinds of variables, each with different scoping and lifetime behavior.

Lexical Variables

The most common type. Lexical variables are block-scoped — they exist only within the block { } where they are declared.

var x = 42
say x         # prints: 42

Static Variables

Static variables are also block-scoped, but they are only initialized once, no matter how many times the block runs.

for k in (1..10) {
    static x = (41 + k)   # assigned only on the first iteration (k=1)
    say x                 # always prints: 42
}

Global Variables

Global variables are accessible from anywhere in your program. Use them sparingly — local or lexical variables are usually a better choice.

global x = 42
say x         # prints: 42

Local Variables

Local variables temporarily override a global variable within a specific block. Once the block ends, the global value is restored.

global x = 42
do {
    local x = 100
    say x         # prints: 100  (local value)
}
say x             # prints: 42   (global value restored)

Variable Scoping

All variables in Sidef are block-scoped. Inner blocks can see variables from outer blocks, but not the other way around. Declaring a variable with the same name in an inner block shadows the outer one — it does not overwrite it.

var x = 'o'

do {
    say x          # o  (outer x)
    var x = 'm'
    say x          # m  (middle x)
    do {
        say x      # m  (still middle x)
        var x = 'b'
        say x      # b  (inner x)
    }
    say x          # m  (inner x is gone)
}

say x              # o  (back to outer x)

Declaring Multiple Variables

You can declare several variables at once using parentheses:

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

You can also provide default values. If the right-hand side is shorter than the left-hand side, the remaining variables keep their defaults:

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

say x    # 666
say y    # 655  (overridden by the assignment)
say z    # 777  (default kept, nothing assigned)

It's also valid to skip the right-hand side entirely, leaving all variables at their declared defaults:

var (
    a = 42,
   *b = (1,2,3,4),          # slurpy array (see below)
   :c = (x => 1, y => 2),   # slurpy hash  (see below)
)

say a   #=> 42
say b   #=> [1,2,3,4]
say c   #=> Hash(x => 1, y => 2)

A previously declared variable can even be referenced as a default value for a later one in the same declaration:

var (
    a = 42,
    b = (10 + a)    # refers to a above
)

say a   #=> 42
say b   #=> 52

Slurpy Variables

Prefixing a variable name with * makes it collect a list into an Array, and : makes it collect key-value pairs into a Hash:

var *arr  = (1,2,3)              # Array
var :hash = (a => 1, b => 2)     # Hash

say arr    # [1,2,3]
say hash   # Hash(a => 1, b => 2)

Working with Variables

Any method called on a variable applies to the value it holds. The variable itself is not changed:

var x = 'sidef'
say x.uc    # SIDEF
say x       # sidef  (unchanged)

To modify the variable in place, append ! to the method name:

var x = 'sidef'
x.uc!
say x       # SIDEF

Arithmetic assignment operators also modify variables in place:

var x = 5
x += 10
say x       # 15

The Defined-Or Assignment Operator (:=)

:= assigns a value only if the variable is currently nil. It's commonly used to provide fallback values:

var x = nil
x := 42     # x is nil, so it gets assigned 42
x := 99     # x is already defined, so this is ignored
say x       # 42

This is especially useful when building a hash of arrays:

var hash = Hash()
hash{:key} := [] << (1,2)
hash{:key} := [] << 3
say hash{:key}    #=> [1,2,3]

The := operator also returns an lvalue, so you can chain further method calls on the result:

var hash = Hash()
hash{:key} := 0 -> max!(10)
hash{:key} := 0 -> max!(42)
say hash{:key}    #=> 42

The Defined-Or Operator (\\)

\\ checks whether a value is defined, without assigning anything:

var x = nil
x \\ say "x is not defined"    # prints: x is not defined

Deleting Variables

Use the del keyword to permanently remove a variable. Any attempt to use it afterward causes a parse-time error:

var foo = 42
del foo
say foo    # parse-time error: attempt to use deleted identifier

The Topic Variable (_)

Every block in Sidef has a pre-declared variable _ (underscore) that holds the current element being iterated over. You rarely need to write _ explicitly, because the unary dot (.) operator is shorthand for _.method:

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

Here .sqrt means _.sqrt, and .log.say means _.log.say. You can also use _ directly to index into arrays or hashes stored in the topic variable:

say [[41,'a'], [42,'b'], [43,'c']].map { .[0] }
#=> [41, 42, 43]

say [Hash(a=>41), Hash(a=>42), Hash(a=>43)].map { .{:a} }
#=> [41, 42, 43]

Special Variables

Two built-in variables are always available:

  • ARGV — command-line arguments passed to the script
  • ENV — environment variables
ARGV.each { |arg| say arg }
say ENV{:HOME}

Magic Variables

Sidef exposes some of Perl's special "magic" variables for low-level control over I/O behavior:

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

File-Handle Constants

These look like variables but are actually I/O handles:

STDERR.say("Some error!")
STDOUT.say("Some output...")
STDIN.readline()    # reads one line from standard input

ARGF reads lines from files passed as arguments, or from standard input if none are given — useful for writing cat-like utilities:

ARGF.each { |line| say line }

DATA reads content placed after __END__ or __DATA__ in the same file:

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

__DATA__
here are
some data
lines

Constants

Sidef provides three ways to declare constants.

const — Runtime Constants

Declared and initialized at runtime. Once set, they cannot be changed.

const pi = 3.14
say pi      # 3.14

When declared inside a function, a const is re-created on each call, but remains immutable within that call:

func f(a) {
    const x = a
    return (x + 2)
}

say f(40)   #=> 42
say f(50)   #=> 52

define — Compile-Time Constants

Evaluated at compile time. The value must be a standalone constant expression. These are the most efficient kind of constant.

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

say (PHI**12 - IHP**12 / (PHI-IHP))   #=> 144

Attempting to reassign a define constant produces a compile-time error (vs. const, which gives a runtime error).

enum — Enumerated Constants

enum declares a sequence of constants with automatically incrementing values (starting at 0):

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

You can specify a starting value. Each subsequent constant is produced by calling .inc on the previous one:

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

All three constant types support the same multi-declaration syntax:

const (        # or: define ( ... ) or static ( ... )
    a = 42,
   *b = (1,2,3,4),
   :c = (x => 1, y => 2),
)

Variable References

You can take a reference to a variable with \ and dereference it with *:

var name = "sidef"
var ref      = \name    # take a reference
var original = *ref     # dereference

References are most useful for passing variables into functions so the function can assign a new value to them:

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

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

The Ref type can be used in type signatures to indicate that a parameter should be a variable reference.


7. Numbers

Sidef has excellent number support. You don't need to worry about separate types for most uses — it handles integers, decimals, and large numbers automatically.

Integer literals

var a = 255          # decimal
var b = 0xff         # hexadecimal (same value: 255)
var c = 0b11111111   # binary      (same value: 255)
var d = 0377         # octal       (same value: 255)

# Underscores make large numbers readable
var million = 1_000_000
var big     = 1_234_567_890

Decimal numbers (exact rational arithmetic)

This is one of Sidef's best features. Decimals are stored as exact fractions internally, so there are no floating-point surprises:

say (0.1 + 0.2)           # 0.3  (exact!)
say (0.1 + 0.2 == 0.3)    # true (unlike most languages)
say (1/3 + 1/3 + 1/3)     # 1    (exact fraction arithmetic)

Big integers

Sidef handles arbitrarily large numbers without any special setup:

say (2 ** 100)        # 1267650600228229401496703205376
say 50.factorial      # a 65-digit number
say 100!              # a 158-digit number (! is factorial postfix)

Useful number methods

say 42.is_even        # true
say 43.is_odd         # true
say (-5).abs          # 5
say 3.14.floor        # 3
say 3.14.ceil         # 4
say 3.75.round        # 4
say 16.sqrt           # 4
say 2.log             # natural logarithm of 2
say 100.log10         # 2  (log base 10)

say 7.is_prime        # true
say 12.is_prime       # false

Special values

say Inf     # infinity
say -Inf    # negative infinity
say NaN     # not a number (result of invalid operations)

8. Strings

A string is a piece of text. Sidef has two kinds of string literals.

Double-quoted strings (support interpolation)

var name = "Alice"
say "Hello, #{name}!"           # Hello, Alice!
say "Two plus two is #{(2+2)}"  # Two plus two is 4
say "Tab:\there"                # Tab:    here
say "Newline:\nSecond line"     # prints on two lines

Inside #{} you can put any Sidef expression:

var items = 5
say "You have #{items} item#{(items == 1 ? '' : 's')}."
# You have 5 items.

Single-quoted strings (no interpolation)

say 'Hello, #{name}!'    # Hello, #{name}!  (printed literally)
say 'No \n escape here'  # No \n escape here

Concatenation

var first = "Hello"
var second = "World"
say (first + ", " + second + "!")    # Hello, World!

String repetition

say ("ha" * 3)       # hahaha
say ("-" * 20)       # --------------------

Multi-line strings (heredocs)

var message = <<'END'
This is a
multi-line string.
No interpolation here.
END

var greeting = <<-"END"
    Hello, #{name}!
    Welcome to Sidef.
    END

9. Booleans

Boolean values are simply true and false.

var is_raining = true
var is_sunny   = false

say is_raining    # true
say is_sunny      # false

Falsy values

In Sidef, the following values are falsy (treated as false in conditions):

  • false
  • nil (the absence of a value)
  • 0
  • "" (empty string)

Everything else is truthy.

if (0) {
    say "This won't print."
}

if ("hello") {
    say "This will print!"    # prints
}

nil — the absence of a value

var x = nil
say x.is_nil    # true

# Conditional assignment: only assign if currently nil
var result = nil
result := compute_value()    # only called if result is nil

10. Operators and Expressions

Arithmetic operators

say (10 + 3)    # 13  addition
say (10 - 3)    # 7   subtraction
say (10 * 3)    # 30  multiplication
say (10 / 3)    # 10/3 (exact fraction)
say (10 % 3)    # 1   modulo (remainder)
say (10 ** 3)   # 1000 exponentiation (power)

Comparison operators

These return true or false:

say (5 == 5)    # true  (equal)
say (5 != 3)    # true  (not equal)
say (5 >  3)    # true  (greater than)
say (5 <  10)   # true  (less than)
say (5 >= 5)    # true  (greater than or equal)
say (5 <= 10)   # true  (less than or equal)

Logical operators

say (true && false)    # false  (and — both must be true)
say (true || false)    # true   (or — at least one must be true)
say (!true)            # false  (not — flips true/false)

Increment and decrement

var n = 5
n++     # n is now 6
n--     # n is now 5
++n     # n is now 6 (prefix: increments before returning value)

String comparison

say ("apple" == "apple")    # true
say ("apple" != "banana")   # true
say ("apple" lt "banana")   # true  (alphabetically less than)
say ("banana" gt "apple")   # true  (alphabetically greater than)

11. Operator Precedence - The Most Important Rule

⚠️ This is the single most important rule to understand in Sidef. Get this wrong and your programs will produce surprising results.

Most languages have complex precedence rules (multiply before add, etc.). Sidef is different: it uses whitespace to determine how operators group.

The rule: no spaces bind tighter than spaces

When operators are written without spaces around them, they are grouped into a single unit first. When operators are written with spaces around them, they are evaluated left to right.

#
# Example 1: spaces between ALL operators → left to right
#
say (1 + 2 * 3 + 4)    # means: ((1+2) * 3) + 4 = 13
#          ↑
#     evaluated as: ((1 + 2) * 3) + 4

#
# Example 2: no spaces → binds tightly
#
say (1 + 2*3 + 4)      # means: 1 + (2*3) + 4 = 11
#         ↑
#     2*3 is one tight unit

#
# Example 3: mixing both
#
say (1+2 * 3+4)        # means: (1+2) * (3+4) = 21
#    ↑↑↑   ↑↑↑
#   tight  tight → each becomes a unit, then * is evaluated

The safest approach: always use parentheses

When in doubt, use explicit parentheses. This always works correctly and makes your intention clear to anyone reading the code:

# These are all clear and unambiguous:
var area    = (length * width)
var average = ((a + b + c) / 3)
var hyp     = ((a**2 + b**2).sqrt)
var tax     = (price * (1 + tax_rate))

Using backslash or dot to override grouping

You can use \ or a leading . to force a different grouping:

say (1 + 2 \* 3)     # means 1 + (2 * 3) = 7
say (1 + 2 .* 3)     # same thing

Summary table

Expression Meaning Result
1 + 2 * 3 (1 + 2) * 3 9
1 + 2*3 1 + (2*3) 7
1+2 * 3+4 (1+2) * (3+4) 21
(1 + 2) * 3 (1 + 2) * 3 9

💡 Best practice: Until you are very comfortable with this rule, wrap every binary operation in its own parentheses. It costs nothing and prevents bugs.


12. Printing Output

say "Hello, World!"       # prints with a newline at the end
print "No newline here"   # prints without a newline
say ""                    # prints a blank line

Printing multiple values

say "Name: ", "Alice"        # Name: Alice
say 1, 2, 3                  # 123
say [1, 2, 3]                # [1, 2, 3]

The > shorthand

> is a shorthand alias for say:

> "Hello!"    # same as: say "Hello!"

Formatted output

var name  = "Alice"
var score = 98.5

say "Player: #{name}, Score: #{score}"
# Player: Alice, Score: 98.5

# Formatting numbers
say "Pi ≈ #{Num.pi.round(4)}"    # Pi ≈ 3.1416
say "Big: #{(10**20).commify}"   # Big: 100,000,000,000,000,000,000

13. Getting Input

Read a line of input from the user with read:

print "What is your name? "
var name = read(String)

say "Hello, #{name}!"

Read a number:

print "Enter a number: "
var n = read(Number)

say "You entered: #{n}"
say "Its square is: #{(n ** 2)}"

A simple interactive example

print "Enter your age: "
var age = read(Number)

if (age >= 18) {
    say "You are an adult."
} else {
    say "You are a minor."
}

14. Control Flow - Making Decisions

if / elsif / else

var temperature = 22

if (temperature > 30) {
    say "It's hot outside!"
} elsif (temperature > 20) {
    say "It's a nice day."
} elsif (temperature > 10) {
    say "It's a bit chilly."
} else {
    say "It's cold — grab a coat!"
}

Postfix if (inline conditions)

For simple one-line conditions, you can put if after the statement:

say "You win!" if (score > 100)
say "Game over." if (lives == 0)
var n = read(Number)
die "Must be positive!" if (n < 0)

Ternary operator

A compact way to pick one of two values:

var age    = 20
var status = (age >= 18 ? "adult" : "minor")
say status    # adult

unless (opposite of if)

var logged_in = false
say "Please log in." unless logged_in

given / when (pattern matching)

given/when is Sidef's switch-like construct. It uses "smart matching" to check each when clause:

var day = "Monday"

given (day) {
    when ("Saturday") { say "Weekend!" }
    when ("Sunday")   { say "Weekend!" }
    when (/^Mon/)     { say "Start of the work week." }
    else              { say "A regular weekday." }
}

You can match against numbers, strings, regex patterns, and more.

with / orwith (checking for defined values)

with runs its block only when the value is defined (not nil):

var result = some_function()

with (result) { |val|
    say "Got a result: #{val}"
}
orwith (fallback_function()) { |val|
    say "Got fallback: #{val}"
}
else {
    say "Nothing was returned."
}

15. Loops

while loop

Keeps running as long as the condition is true:

var count = 1
while (count <= 5) {
    say "Count: #{count}"
    count++
}

Output:

Count: 1
Count: 2
Count: 3
Count: 4
Count: 5

do-while loop

Runs the body at least once before checking the condition:

var n = 0
do {
    n++
    say n
} while (n < 3)
# prints: 1, 2, 3

for loop — iterating a range

for i in (1..5) {
    say "Step #{i}"
}

for loop — iterating an array

var fruits = ["apple", "banana", "cherry"]
for fruit in fruits {
    say "I like #{fruit}"
}

.times — repeat N times

3.times {
    say "Hello!"
}
# prints Hello! three times

5.times { |i|
    say "Iteration #{i}"    # i goes from 0 to 4
}

.each — iterate with a block

[10, 20, 30].each { |n|
    say "Value: #{n}"
}

loop — infinite loop with break

var n = 1
loop {
    say n
    n++
    break if (n > 5)
}

Loop control: next and break

# next: skip to the next iteration
for i in (1..10) {
    next if (i % 2 == 0)    # skip even numbers
    say i
}
# prints: 1, 3, 5, 7, 9

# break: exit the loop early
for i in (1..100) {
    break if (i > 5)
    say i
}
# prints: 1, 2, 3, 4, 5

Ranges with custom steps

for i in ((0..20).by(5)) {
    say i    # 0, 5, 10, 15, 20
}

for i in ((10^..1).by(-1)) {
    say i    # 10, 9, 8, ..., 1
}

Counting upward: .upto / downward: .downto

1.upto(5) { |i| say i }     # 1 2 3 4 5
5.downto(1) { |i| say i }   # 5 4 3 2 1

16. Functions

Functions let you name and reuse pieces of code.

Basic function

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

greet("Alice")    # Hello, Alice!
greet("Bob")      # Hello, Bob!

Return values

The last expression in a function is returned automatically. You can also use return explicitly:

func add(a, b) {
    (a + b)    # returned automatically
}

func multiply(a, b) {
    return (a * b)    # explicit return
}

var sum     = add(3, 4)         # 7
var product = multiply(3, 4)    # 12
say sum      # 7
say product  # 12

Default parameters

func greet(name = "World", greeting = "Hello") {
    say "#{greeting}, #{name}!"
}

greet()                        # Hello, World!
greet("Alice")                 # Hello, Alice!
greet("Bob", "Good morning")   # Good morning, Bob!

Named parameters

You can pass arguments by name, in any order:

func make_box(width, height, depth) {
    say "Box: #{width} × #{height} × #{depth}"
}

make_box(width: 10, depth: 5, height: 3)
# Box: 10 × 3 × 5

Variadic functions (any number of arguments)

func sum(*numbers) {
    numbers.reduce(0, { |acc, n| (acc + n) })
}

say sum(1, 2, 3)          # 6
say sum(10, 20, 30, 40)   # 100

Type constraints

You can require arguments to be a specific type:

func square(Number n) {
    (n ** 2)
}

func shout(String s) {
    s.uc + "!!!"
}

say square(5)       # 25
say shout("hello")  # HELLO!!!

If you pass the wrong type, Sidef will raise an error.

Recursion

A function can call itself — this is called recursion:

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

say factorial(5)    # 120
say factorial(10)   # 3628800

Memoization with is cached

is cached makes Sidef automatically remember results so the same calculation is never repeated:

func fib(n) is cached {
    return n if (n <= 1)
    (fib((n - 1)) + fib((n - 2)))
}

say fib(10)     # 55
say fib(50)     # 12586269025  (fast, thanks to caching)
say fib(100)    # 354224848179261915075

Without is cached, fib(50) would take an impossibly long time.

Anonymous functions (lambdas)

Functions can be stored in variables:

var double = { |n| (n * 2) }
var square = { |n| (n ** 2) }
var add    = { |a, b| (a + b) }

say double(5)      # 10
say square(4)      # 16
say add(3, 7)      # 10

Closures

A closure is a function that "remembers" the variables from where it was created:

func make_counter(start = 0) {
    var count = start
    func {
        count++
        say "Count: #{count}"
    }
}

var counter_a = make_counter()
var counter_b = make_counter(10)

counter_a()    # Count: 1
counter_a()    # Count: 2
counter_b()    # Count: 11
counter_a()    # Count: 3  (independent from counter_b)

17. Arrays

An array is an ordered list of values. Arrays can hold any mix of types.

Creating arrays

var fruits   = ["apple", "banana", "cherry"]
var numbers  = [1, 2, 3, 4, 5]
var mixed    = [1, "hello", true, 3.14]
var empty    = []

Accessing elements

Arrays are zero-indexed — the first element is at index 0:

var fruits = ["apple", "banana", "cherry", "date"]

say fruits[0]     # apple   (first)
say fruits[1]     # banana  (second)
say fruits[-1]    # date    (last)
say fruits[-2]    # cherry  (second to last)

Modifying arrays

var arr = [1, 2, 3]

arr.push(4)        # add to end   → [1, 2, 3, 4]
arr.pop            # remove last  → [1, 2, 3]
arr.unshift(0)     # add to start → [0, 1, 2, 3]
arr.shift          # remove first → [1, 2, 3]

arr[1] = 99        # replace by index → [1, 99, 3]

Useful array methods

var nums = [3, 1, 4, 1, 5, 9, 2, 6]

say nums.length           # 8
say nums.sort             # [1, 1, 2, 3, 4, 5, 6, 9]
say nums.reverse          # [6, 2, 9, 5, 1, 4, 1, 3]
say nums.sum              # 31
say nums.min              # 1
say nums.max              # 9
say nums.uniq             # [3, 1, 4, 5, 9, 2, 6]  (remove duplicates)
say nums.first            # 3
say nums.last             # 6
say nums.first(3)         # [3, 1, 4]

Functional array methods

These are the methods you'll use most often with arrays:

map — transform every element:

var numbers = [1, 2, 3, 4, 5]
var squared = numbers.map { |n| (n ** 2) }
say squared    # [1, 4, 9, 16, 25]

var names   = ["alice", "bob", "carol"]
var uppered = names.map { .uc }
say uppered    # ["ALICE", "BOB", "CAROL"]

grep — keep only elements that match a condition:

var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

var evens  = numbers.grep { .is_even }
var odds   = numbers.grep { .is_odd }
var big    = numbers.grep { |n| (n > 5) }

say evens    # [2, 4, 6, 8, 10]
say odds     # [1, 3, 5, 7, 9]
say big      # [6, 7, 8, 9, 10]

reduce — combine all elements into one value:

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

var sum     = numbers.reduce(0, { |acc, n| (acc + n) })
var product = numbers.reduce(1, { |acc, n| (acc * n) })

say sum       # 15
say product   # 120

each — loop over every element:

["red", "green", "blue"].each { |color|
    say "Color: #{color}"
}

each_with_index — loop with both index and value:

["a", "b", "c"].each_with_index { |val, i|
    say "#{i}: #{val}"
}
# 0: a
# 1: b
# 2: c

Joining and splitting

var words = ["Hello", "World", "from", "Sidef"]
say words.join(" ")     # Hello World from Sidef
say words.join(", ")    # Hello, World, from, Sidef
say words.join          # HelloWorldfromSidef

# word arrays with %w notation (shorthand for array of strings)
var fruits = %w(apple banana cherry)
# same as: ["apple", "banana", "cherry"]

Slices

var arr = [10, 20, 30, 40, 50]

say arr[1..3]      # [20, 30, 40]  (indices 1 to 3)
say arr[0..^3]     # [10, 20, 30]  (indices 0 to 2, exclusive end)
say arr[2..]       # [30, 40, 50]  (from index 2 to the end)

Checking membership

var fruits = ["apple", "banana", "cherry"]

say fruits.contains("banana")    # true
say fruits.contains("grape")     # false
say fruits.index("cherry")       # 2  (position of element)

Ranges as arrays

say (1..5).to_a           # [1, 2, 3, 4, 5]
say (1..10).grep { .is_prime }.to_a    # [2, 3, 5, 7]

18. Hashes (Dictionaries)

A hash stores key-value pairs. Keys are usually strings (or symbols), and values can be anything.

Creating a hash

var person = Hash(
    name => "Alice",
    age  => 30,
    city => "London",
)

Accessing values

say person{:name}    # Alice
say person{:age}     # 30
say person{:city}    # London

Adding and changing values

person{:email} = "[email protected]"    # add new key
person{:age}   = 31                     # update existing key

Checking if a key exists

say person.has(:name)       # true
say person.has(:phone)      # false

Removing a key

person.delete(:city)

Listing keys and values

say person.keys      # ["name", "age", "email"]  (order may vary)
say person.values    # ["Alice", 31, "[email protected]"]
say person.len       # 3

Iterating a hash

person.each { |key, value|
    say "#{key}: #{value}"
}

Practical hash example

# Count word frequencies
var text  = "the cat sat on the mat the cat"
var words = text.split(" ")
var freq  = Hash()

words.each { |word|
    freq{word} := 0    # set to 0 if not yet defined
    freq{word}++
}

freq.keys.sort.each { |word|
    say "#{word}: #{freq{word}}"
}
# cat: 2
# mat: 1
# on:  1
# sat: 1
# the: 3

19. String Operations

Common string methods

var s = "Hello, World!"

say s.uc            # HELLO, WORLD!  (uppercase)
say s.lc            # hello, world!  (lowercase)
say s.length        # 13
say s.reverse       # !dlroW ,olleH
say s.trim          # removes leading/trailing whitespace

say s.contains("World")      # true
say s.begins_with("Hello")   # true
say s.ends_with("!")         # true
say s.index("World")         # 7  (position where it starts)

Searching and replacing

var text = "I like cats and cats like me."

say text.sub("cats", "dogs")          # I like dogs and cats like me.  (first only)
say text.gsub("cats", "dogs")         # I like dogs and dogs like me.  (all)
say text.gsub(/cats?/, "animals")     # with regex pattern

Splitting and joining

var csv = "Alice,30,London"
var parts = csv.split(",")
say parts[0]    # Alice
say parts[1]    # 30
say parts[2]    # London

say parts.join(" | ")    # Alice | 30 | London

Extracting substrings

var s = "Hello, World!"

say s[0..4]       # Hello
say s[7..11]      # World
say s[0, 5]       # Hello  (start, length)

Regular expressions (pattern matching)

Regular expressions let you match complex text patterns:

var email = "[email protected]"

if (email =~ /\w+@\w+\.\w+/) {
    say "Valid email format."
}

# Capture parts of a match
var date  = "2024-03-15"
var match = date.match(/(\d{4})-(\d{2})-(\d{2})/)
say "Year:  #{match[0]}"    # 2024
say "Month: #{match[1]}"    # 03
say "Day:   #{match[2]}"    # 15

Useful string conversions

say "42".to_i       # converts string to integer: 42
say "3.14".to_r     # converts string to number:  3.14
say 42.to_s         # converts number to string: "42"
say 255.to_s(16)    # convert to hex string: "ff"
say 255.to_s(2)     # convert to binary string: "11111111"

20. Working with Files

Reading a file

# Read the entire file as a string
var content = File("myfile.txt").read
say content

# Read line by line (memory efficient for large files)
File("myfile.txt").each_line { |line|
    say line.trim
}

# Read all lines into an array
var lines = File("myfile.txt").lines
say "File has #{lines.len} lines."

Writing a file

# Write (overwrites any existing content)
File("output.txt").write("Hello, File!\n")

# Append (adds to the end without erasing)
File("output.txt").append("Second line.\n")

Checking if a file exists

var f = File("data.txt")

if (f.exists) {
    say "File found! Size: #{f.size} bytes."
    var content = f.read
} else {
    say "File not found."
}

Practical file example

# Save a shopping list to a file and read it back
var list = ["apples", "bread", "milk", "eggs"]
File("shopping.txt").write(list.join("\n") + "\n")

say "Saved shopping list. Reading it back:"
File("shopping.txt").each_line { |line|
    say "- #{line.trim}"
}

21. Error Handling

Use try and catch to handle errors gracefully:

try {
    var result = (10 / 0)
    say result
}
catch { |error|
    say "An error occurred: #{error}"
}

Raising errors yourself

func divide(a, b) {
    die "Cannot divide by zero!" if (b == 0)
    (a / b)
}

try {
    say divide(10, 2)    # 5
    say divide(10, 0)    # raises error
}
catch { |msg|
    say "Error: #{msg}"
}

Assertions (for debugging)

var x = 5
assert(x > 0, "x must be positive")
assert_eq(x, 5, "x must be 5")
assert_ne(x, 0, "x must not be zero")

If an assertion fails, the program stops with a clear message.


22. A Taste of Sidef's Math Powers

One of Sidef's greatest strengths is its built-in mathematical capabilities. Here's a brief preview — these work out of the box, no libraries needed.

Primes

say 17.is_prime           # true
say 10.primes             # [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
say primes(100, 150)      # all primes between 100 and 150
say 1000.prime            # 7919  (the 1000th prime)
say prime_count(10**6)    # 78498 (number of primes up to a million)

Factorization

say factor(360)            # [2, 2, 2, 3, 3, 5]
say factor_exp(360)        # [[2,3],[3,2],[5,1]]  (prime, exponent pairs)
say divisors(36)           # [1, 2, 3, 4, 6, 9, 12, 18, 36]
say euler_phi(36)          # 12  (Euler's totient)

GCD, LCM

say gcd(48, 18)    # 6
say lcm(4,  6)     # 12

Big numbers

say (2 ** 1000)           # a 302-digit number
say 100.factorial         # a 158-digit number
say (10 ** 100)           # a googol

Mathematical constants and functions

say Num.pi              # π ≈ 3.14159265358979...
say Num.e               # e ≈ 2.71828182845904...
say sqrt(2)             # √2 ≈ 1.41421356237309...
say sin(Num.pi / 2)     # 1
say cos(0)              # 1
say log(Num.e)          # 1
say (2.718281828).log   # ≈ 1  (natural log)

Number bases

say 255.to_s(2)     # "11111111"  (binary)
say 255.to_s(16)    # "ff"        (hexadecimal)
say 255.to_s(8)     # "377"       (octal)
say "ff".to_i(16)   # 255         (hex string to integer)

23. Beginner Projects to Try

Work through these projects to practice what you've learned. They're ordered roughly from easiest to most challenging.


Project 1: Temperature Converter

func celsius_to_fahrenheit(c) {
    ((c * 9/5) + 32)
}

func fahrenheit_to_celsius(f) {
    ((f - 32) * 5/9)
}

print "Enter temperature in Celsius: "
var c = read(Number)

say "#{c}°C = #{celsius_to_fahrenheit(c).round(2)}°F"
say "#{c}°C = #{(c + 273.15).round(2)} K"

Project 2: FizzBuzz

A classic exercise: for each number 1–100, print "Fizz" if divisible by 3, "Buzz" if by 5, "FizzBuzz" if by both, otherwise the number:

for i in (1..100) {
    say (
        ((i % 15) == 0) ? "FizzBuzz" :
        ((i % 3)  == 0) ? "Fizz"     :
        ((i % 5)  == 0) ? "Buzz"     :
        i
    )
}

Project 3: Simple Calculator

func calculate(a, op, b) {
    given (op) {
        when ("+") { (a + b) }
        when ("-") { (a - b) }
        when ("*") { (a * b) }
        when ("/") {
            die "Cannot divide by zero!" if (b == 0)
            (a / b)
        }
        else { die "Unknown operator: #{op}" }
    }
}

print "First number:  "
var a = read(Number)
print "Operator (+, -, *, /): "
var op = read(String)
print "Second number: "
var b = read(Number)

try {
    say "Result: #{calculate(a, op, b)}"
}
catch { |msg|
    say "Error: #{msg}"
}

Project 4: Guess the Number

var secret = (1..100).rand    # random number between 1 and 100
var guesses = 0

say "I'm thinking of a number between 1 and 100."
say "Can you guess it?"

loop {
    print "Your guess: "
    var guess = read(Number)
    guesses++

    if (guess < secret) {
        say "Too low! Try higher."
    } elsif (guess > secret) {
        say "Too high! Try lower."
    } else {
        say "Correct! You got it in #{guesses} guess#{(guesses == 1 ? '' : 'es')}!"
        break
    }
}

Project 5: Simple Statistics

func stats(data) {
    var n    = data.len
    var mean = (data.sum / n)

    var sorted   = data.sort
    var median   = (n.is_odd
        ? sorted[n / 2]
        : ((sorted[(n/2) - 1] + sorted[n/2]) / 2)
    )

    var variance = (data.map { |x| ((x - mean)**2) }.sum / n)
    var std_dev  = variance.sqrt

    Hash(
        count   => n,
        sum     => data.sum,
        mean    => mean.round(4),
        median  => median,
        min     => data.min,
        max     => data.max,
        std_dev => std_dev.round(4),
    )
}

var data = [4, 8, 15, 16, 23, 42]
var s    = stats(data)

say "Count:   #{s{:count}}"
say "Sum:     #{s{:sum}}"
say "Mean:    #{s{:mean}}"
say "Median:  #{s{:median}}"
say "Min:     #{s{:min}}"
say "Max:     #{s{:max}}"
say "Std Dev: #{s{:std_dev}}"

Project 6: Fibonacci Sequence

# Method 1: Simple recursive with caching
func fib(n) is cached {
    return n if (n <= 1)
    (fib((n - 1)) + fib((n - 2)))
}

say "First 15 Fibonacci numbers:"
say (0..14).map { |n| fib(n) }

# Method 2: Iterative
func fib_sequence(count) {
    var result = [0, 1]
    while (result.len < count) {
        result.push((result[-1] + result[-2]))
    }
    result.first(count)
}

say fib_sequence(20)

Project 7: Prime Explorer

say "=== Prime Number Explorer ==="
say ""

# First 20 primes
say "First 20 primes:"
say 20.primes
say ""

# Check if numbers are prime
[2, 7, 13, 25, 97, 100, 101].each { |n|
    var status = n.is_prime ? "prime" : "not prime"
    say "#{n} is #{status}"
}
say ""

# Factorize some numbers
[12, 60, 100, 360, 2310].each { |n|
    say "#{n} = #{factor(n).join(' × ')}"
}
say ""

# Twin primes (pairs that differ by 2)
say "Twin prime pairs up to 100:"
var prev = 2
primes(3, 100).each { |p|
    say "(#{prev}, #{p})" if ((p - prev) == 2)
    prev = p
}

Project 8: Word Frequency Counter

# Count and display word frequencies from a string

var text = <<'END'
To be or not to be that is the question
Whether tis nobler in the mind to suffer
The slings and arrows of outrageous fortune
Or to take arms against a sea of troubles
END

var words = text.lc.split(/\W+/).grep { .len > 0 }
var freq  = Hash()

words.each { |w|
    freq{w} := 0
    freq{w}++
}

say "Word frequencies (sorted by count, descending):"
say "=" * 35

freq.keys
    .sort_by { |k| -freq{k} }
    .first(10)
    .each { |word|
        var count = freq{word}
        var bar   = "█" * count
        say "#{ '%-12s' % word } #{bar} (#{count})"
    }

Project 9: Sierpinski Triangle

This draws a famous fractal pattern using simple string operations:

func sierpinski(n) {
    var rows = ["*"]
    n.times { |i|
        var sp   = (" " * (2**i))
        rows = (rows.map { |r| (sp + r + sp) } +
                rows.map { |r| (r + " " + r) })
    }
    rows.join("\n")
}

say sierpinski(4)

24. Where to Learn More

You've covered the fundamentals! Here's where to go next.

Official Documentation

Resource Description
📘 Sidef GitBook The complete language reference — covers everything
📄 PDF Book The full book in PDF format for offline reading
📝 Advanced Tutorial An advanced tutorial covering the full language
🔢 Number Theory Tutorial Deep dive into Sidef's mathematical superpowers

Example Code

Resource Description
📂 sidef-scripts Hundreds of real Sidef programs — the best way to learn by reading
🌹 RosettaCode — Sidef Classic programming tasks solved in Sidef, side-by-side with other languages

Community

Resource Description
💬 GitHub Discussions Ask questions, share ideas, get help
🐛 GitHub Issues Bug reports and feature requests
📦 MetaCPAN CPAN package page with additional documentation

Try Without Installing

https://tio.run/#sidef — Run Sidef code instantly in your browser.

What to explore next

Once you're comfortable with the basics here, the natural next steps are:

  1. Object-oriented programming — classes, inheritance, methods
  2. Modules and namespaces — organizing larger programs
  3. Lazy evaluation — working with infinite sequences efficiently
  4. Number theory — Sidef's extraordinary built-in math functions
  5. Perl module integration — using any CPAN library from Sidef

All of these are covered in detail in the Sidef Advanced Guide and the official book.


You're ready to start coding! Happy hacking with Sidef. 🚀

Start with small programs, experiment freely in the REPL, and don't be afraid to break things — that's how you learn.