deimos-lang/dm-book/src/getting_started.md
2026-03-26 16:29:42 -05:00

3.6 KiB

Chapter 1: Getting Started

Deimos can be run both interactively via a read-eval-print loop (REPL) or by compiling and running a given source file.

REPL

The REPL allows interactive programming similar to classic functional languages as well as Ruby. To start the REPL, run:

dm repl

Basics

All statements and expressions in the REPL are scoped under a synthetic function; in other words, variables persist through each input:

> let x = 2
> x
2
> let y = 3
> x + y
5

In addition to statements and expressions, functions can be defined in scope:

> fn double(x: Int) x * 2 end
> double(4)
8

Note that variables in the "top" scope are not available inside defined functions:

> let x = 7
> fn doubleX() x * 2 end
Error: Symbol x could not be found.

This requires the use of closures, which can "capture" variables in their containing scope:

> let x = 42
> let doubleX = { x * 2 }
> doubleX()
84

Note the difference in syntax between function declarations and closure declarations. As we will later see, closures are powerful constructs.

Introduction to Classes

Classes can also be defined in the REPL:

> class Dog \
  pub name: String \
  pub ctor(name: String) \
    self.name = name \
  end \
end

A few things are going on here. First, note the use the backslash to indicate that we are not done with our input (normally, a newline without a preceding backslash will cause the interpreter to consume everything input so far). Next, note our syntax for declaring a class, with hints of Rust and Ruby:

  • class keyword: the usual meaning
  • pub keyword: makes a property or method available outside the class (i.e., non-private)
  • : Type annotations: indicates the type of a property, parameter, or variable.
  • ctor keyword: marks a constructor. Note: Classes can only have one constructor.
  • self keyword, followed by property name: like the usual construct in OOP languages.

Once we've defined our class, we can use it:

> let bear = Dog("Bear")
> let skye = Dog("Skye")
> bear.name
Bear
> skye.name
Skye

Note above how we construct instances of the class by "calling" the class, exactly like we do in Kotlin.

Class Declaration Short Form

The above class example is a bit verbose; Deimos offers the following equivalent, inspired by Kotlin:

class Dog(pub name: String) end

This is especially useful in the REPL, where we may just want to declare a data type for doing some data processing.

> class Point(pub x: Int, pub y: Int) end
> fn double(p: Point) Point(p.x * 2, p.y * 2) end
> let a = Point(3, 4)
> double(a)
Point(6, 8)
> let b = Point(5, 6)
> double(double(b))
Point(20, 24)

Compiling and Running Files

To compile and run a file, do:

dm run <your file name here>.dm

Note that the .dm extension is required in the command name (unlike java).

The above command will compile and immediately run the file. In order for a Deimos file to be executable, it must contain a main function like the following:

fn main()
  println("Hello, Deimos!")
end

Unlike the REPL, statements and expressions are not allowed in the top-level scope; they must be contained in a function or method:

let x = 10 // ERROR! Will not parse.

However, functions, classes, and other top-level constructs are allowed:

fn double(p: Point) -> Point
  Point(p.x * 2, p.y * 2)
end

class Point(x: Int, y: Int) end

fn main()
  let a = Point(1, 2)
  let b = Point(3, 4)
  println(double(a)) // Point(2, 4)
  println(double(b)) // Point(6, 8)
end
let f = { a, b -> a + b } << 4
let x = f(5)
println(x) // 9