Also available at: https://ninjas.cl/blog/odin-s-elixir/
What is Odin?
Odin is a system level language that is a direct alternative to Rust, Zig or C. Creating an alternative to C language is nearly an impossible task, because more than 40 years of history of C. But at least a new programming language can be an alternative to C in modern systems and architectures, as the website says "Odin is the C alternative for the Joy of Programming". The official implementation of Odin currently supports: amd64, arm64/aarch64 (Rapsberry Pi), and wasm32/wasm64p32. The language borrows heavily from: Pascal, C, Go, Oberon-2, Newsqueak, GLSL.
What are the guiding principles behind the design of Odin?
Odin is a general-purpose programming language with distinct typing built for high performance, modern systems and data-oriented programming.
- Simplicity and readability.
- Minimal: there ought to be one way to write something.
- Striving for orthogonality.
- Programs are about transforming data into other forms of data.
- Code is about expressing algorithms—not the type system.
- There is embedded knowledge and wisdom in older programming languages.
- The entire language specification should be possible to be memorized by a mere mortal.
Hello World in Odin
package main
import "core:fmt"
main :: proc() {
fmt.println("Hellope!")
}
Fibonacci
fibonacci :: proc(n: int) -> int {
switch {
case n < 1:
return 0
case n == 1:
return 1
}
return fibonacci(n-1) + fibonacci(n-2)
}
fmt.println(fibonacci(3)) // 2
Odin's Elixir?
Odin and Elixir is a match made in Valhalla. With Elixir we can create robust and reliable systems and with Odin we can create powerful and secure low level extensions with FFI, or self contained terminal applications and libraries that can be shared with other programming languages, instead of using alternatives such as C, Rust, Zig or Go.
This is a video how to integrate Odin and Elixir:
Elixir (2011) and Odin (2016), despite the fact that they are very modern languages, are quite small. You can read through the language guide in a few hours and have a pretty good grasp on the core concepts. This leads to much quicker mastery.
Treasure Hunt
Let's do a simple exercise with CRC (Constructors
, Reducers
and Converters
) a principle of code organization made by Bruce Tate.
Constructors
: Are procedures (or functions) that creates and sets data structures that will be passed down toReducers
andConverters
. This data structure is known as theaccumulator
ortoken
.Reducers
: These procedures take theaccumulator
and "reduce it", applying a pipeline of different operations and procedures until it reaches a state ready for theConverter
.Converter
: Takes the accumulator and "convert it" to a final format, that is ready for displaying to the user or pass it to another pipeline of CRC. Example would be a procedure that takes ajson
data structure and convert it tostring
, this string will be passed down to the pipeline for saving the contents to a file in disk.
Thanks to this form of organization, we can see our code in a coherent and unified way. By having a common data structure with several procedures (or functions), we can perform operations and express ourselves with greater readability. Our systems will be easier to understand and maintain in the future. Consistency is an important quality factor in our systems and using CRC is a great tool to achieve that. The really valuable idea in this principle is that it's extremely handy if you can clearly separate procedures that change state from those that don't. This is because you can use creators procedures in many situations with much more confidence, introducing them anywhere, changing their order. You have to be more careful with reducers and converters.
Elixir Version
defmodule Treasure do
# Creator
def new(x, y), do: {x, y}
def new, do: new(0, 0)
# Reducers
def north({x, y}), do: {x, y - 1}
def south({x, y}), do: {x, y + 1}
def east({x, y}), do: {x - 1, y}
def west({x, y}), do: {x + 1, y}
# Converter
def show({x, y}), do: "The treasure is located at (#{x},#{y})"
end
Treasure.new()
|> north()
|> north()
|> north()
|> north()
|> west()
|> west()
|> west()
|> south()
|> east()
|> east()
|> east()
|> show() # The treasure is located at (0,-3)
Odin Version 1
package CRC
import "core:fmt"
Treasure :: struct {
x: int,
y: int
}
// Constructors
new :: proc(x: int, y: int) -> Treasure {
treasure : Treasure
treasure.x = x
treasure.y = y
return treasure
}
empty :: proc() -> Treasure {
return new(0, 0)
}
// Reducers
north :: proc(treasure : Treasure) -> Treasure {
return new(treasure.x, treasure.y - 1)
}
south :: proc(treasure : Treasure) -> Treasure {
return new(treasure.x, treasure.y + 1)
}
east :: proc(treasure : Treasure) -> Treasure {
return new(treasure.x - 1, treasure.y)
}
west :: proc(treasure : Treasure) -> Treasure {
return new(treasure.x + 1, treasure.y)
}
// Converter
show :: proc (treasure : Treasure) -> (string, int, int) {
return "The treasure is located at (%d, %d)", treasure.x, treasure.y
}
// Pipeline
main :: proc() {
treasure := empty() // (0, 0)
treasure = north(treasure) // (0, -1)
treasure = north(treasure) // (0, -2)
treasure = north(treasure) // (0, -3)
treasure = north(treasure) // (0, -4)
treasure = west(treasure) // (1, -4)
treasure = west(treasure) // (2, -4)
treasure = west(treasure) // (3, -4)
treasure = south(treasure) // (3, -3)
treasure = east(treasure) // (2, -3)
treasure = east(treasure) // (1, -3)
treasure = east(treasure) // (0, -3)
out, x, y := show(treasure)
fmt.printfln(out, x, y) // The treasure is at (0, -3)
}
Odin Version 2
Odin provides another interesting way of approaching this challenge. This is using Array programming techniques where basic operations (+, -, *, etc.) on two fixed-size arrays of the same length are done element-wise. One of the goodies of Odin.
Treasure :: [2]int
NORTH :: Treasure{ 0, -1}
SOUTH :: Treasure{ 0, +1}
EAST :: Treasure{-1, 0}
WEST :: Treasure{+1, 0}
treasure := \
NORTH + // (0, -1)
NORTH + // (0, -2)
NORTH + // (0, -3)
NORTH + // (0, -4)
WEST + // (1, -4)
WEST + // (2, -4)
WEST + // (3, -4)
SOUTH + // (3, -3)
EAST + // (2, -3)
EAST + // (1, -3)
EAST // (0, -3)
This have the strong advantage that if something like a string is used instead of numbers to generate the treasure map, it won't compile. The validation is then at compiler level instead of at runtime.
Final Thoughts
Odin and Elixir provides powerful tools to create robust systems, low level, bindings and perfomant code. Any Elixir developer would benefit learning Odin and any Odin developer would benefit learning Elixir.
Odin and Elixir is truly a match made in Valhalla.
Top comments (0)