🚀 Crystal Introduction

🎯 Complete Definition

Crystal is a statically typed, compiled language with Ruby‑inspired syntax, but compiles to efficient native code via LLVM. Created by Ary Borenszweig and Juan Wajnerman in 2014, it combines the productivity of a high‑level language with the performance of C. Crystal features global type inference (no need to write types explicitly), nullable reference safety, and a powerful macro system. It is designed for building fast, concurrent systems with a clean syntax.

🔬 Core Characteristics

  • Compiled (LLVM): Executes at speeds close to C, with link‑time optimisation.
  • Statically typed: All types are known at compile time; type inference makes it feel dynamic.
  • Ruby‑inspired syntax: Clean, readable, and expressive – but compiled, not interpreted.
  • Nil‑safe: The compiler forces handling of nil through Nil union checks.
  • Concurrency: Lightweight fibers (like Go routines) with channel‑based communication.
  • C bindings: Easy to call C libraries without writing glue code.
  • Macros: Compile‑time code generation and metaprogramming.

📊 Industry Usage

Crystal powers high‑performance web apps (Kemal, Amber frameworks), API microservices, and systems programming where developer productivity and speed are both critical. Used by companies like Manas.Tech, PlaceOS, and many startups for its blend of productivity and native performance.

puts "🔮 Crystal 1.12 - CodeOrbitPro Pro Track" # Version information at compile time {% puts "Compiling with Crystal {{ `crystal version` }}" %} # Simple example name = "Crystal" puts "Hello, #{name}!"

📊 Variables & Types

🎯 Complete Definition

Variables in Crystal are statically typed but type‑inferred. Once a variable is assigned a value, its type is fixed (unless declared with a union). The language provides primitive types (Int32, Int64, Float64, Bool, Char, String), as well as compound types (Array, Hash, Tuple, NamedTuple, Set). Type annotations are optional thanks to global type inference.

🏷️ Core Type System

  • Int: Int8, Int16, Int32, Int64, UInt8 … UInt64 – fixed size, no overflow checks.
  • Float: Float32, Float64 – IEEE 754.
  • Bool: true / false.
  • Char: single Unicode codepoint (e.g. 'A', 'é').
  • String: UTF‑8 encoded, immutable.
  • Symbol: Interned, immutable constants (:name).
  • Nil: the only instance is nil – must be handled explicitly.
  • Union types: e.g. Int32 | String – automatically created.

🔧 Advanced Features

Type aliases (alias Age = Int32), typeof, as casting, is_a? runtime checks, responds_to?, and Pointer for low‑level memory access. Everything is an object – even primitives have methods.

# Type inference age = 30 # Int32 name = "Crystal" # String rating = 4.9 # Float64 # Explicit union value : Int32 | String = 42 value = "now string" # Type inspection puts typeof(age) # Int32 puts age.class # Int32 # Nilable (T? is sugar for T | Nil) maybe : String? = nil maybe = "hello" puts maybe.not_nil! if maybe

🔢 Operators

🎯 Complete Definition

Operators in Crystal are methods (like Ruby) but compiled. Most operators can be overloaded via method definitions. Standard categories: arithmetic, comparison, bitwise, logical, assignment, and range. Operator precedence follows C/Ruby conventions.

📋 Operator Categories

  • Arithmetic: +, -, *, /, %, ** (power, as method)
  • Comparison: ==, !=, <, >, <=, >=, <=> (spaceship)
  • Bitwise: &, |, ^, ~, <<, >>
  • Logical: &&, ||, ! (short‑circuit)
  • Assignment: =, +=, -=, *=, etc.
  • Range: .. (inclusive) , ... (exclusive end)
  • Misc: &+ (wrapping addition), | (union type), -> (literal pointer)

⚡ Precedence & Overloading

Same as Ruby: `**` highest, then unary `+ - ! ~`, then `* / %`, then `+ -`, then bit shifts, then bitwise AND, then `| ^`, then comparison, then `&&`, then `||`, then ranges, then assignment. Overload by defining method e.g. `def +(other)`.

puts 10 + 5 # 15 puts 10 / 3 # 3 (integer division) puts 10.0 / 3 # 3.33333 puts 2 ** 10 # 1024 (power) puts 5 & 3 # 1 (bitwise AND) # Range operator range = 1..5 range.each { |i| print i } # 12345 # Logical short‑circuit x = nil y = x || 42 # 42 puts y

🔀 Control Flow

🎯 Complete Definition

Control flow uses if, unless, case, and ternary expressions. Crystal adds when (inside case) with flexible matching, and the in operator for membership checks. All conditionals return values (like Ruby). No `switch`, but case covers it with powerful type matching.

🏗️ Structures

  • if / unless – also postfix: `puts "ok" if ok`
  • ternary: `condition ? true_expr : false_expr`
  • case … when – matches with `===` (case equality)
  • in – checks membership: `x in 1..10`
  • as – safe type casting in control flow
score = 85 grade = if score >= 90 "A" elsif score >= 80 "B" else "C" end puts grade # B # Case with multiple when case score when 90..100 puts "Excellent" when 80...90 puts "Good" else puts "Keep trying" end # Postfix unless debug = false puts "Debugging" unless debug

🔄 Loops

🎯 Complete Definition

Loops in Crystal use while, until, and iterators over ranges/enumerables. There's no traditional `for` loop – instead you call `.each` on collections. loop creates an infinite loop with `break`/`next`. Crystal's loops are expressions (they return the value passed to `break`).

🏗️ Loop Forms

  • while condition – runs while truthy
  • until condition – runs while falsy
  • loop – infinite, use `break`
  • iterators: `each`, `times`, `upto`, `downto`, `step`
  • break / next – with optional value
# while i = 0 while i < 3 puts i i += 1 end # times 3.times { |j| puts j } # each over range (1..4).each { |n| print n } # 1234 # loop with break x = 0 loop do x += 1 break x if x > 2 end puts x # 3

⚙️ Methods & Def

🎯 Complete Definition

Methods are defined with def and can have type restrictions, default parameters, splats (*args), double splats (**options), and named arguments. They return the last expression implicitly. Methods can be overloaded by arity and type restrictions (multiple definitions with same name but different signatures).

🏗️ Method Features

  • def name(arg : Type) : ReturnType – optional type restrictions
  • default values: def greet(name = "world")
  • splat: def sum(*numbers) collects into Tuple
  • named args: def foo(bar, baz) called as foo(bar: 1, baz: 2)
  • block parameter: &block yields to block
  • forwarding: def foo(*args, **options, &block)
def add(a : Int32, b : Int32) : Int32 a + b end def multiply(a, b) # inferred return type a * b end def greet(name = "Crystal") "Hello, #{name}!" end def sum(*nums) nums.sum end puts add(5, 3) # 8 puts greet("Pro") # Hello, Pro! puts sum(1,2,3,4) # 10

📋 Structs & Tuples

🎯 Complete Definition

Structs are value types (stack allocated, copied on assignment) defined with struct. They are ideal for small, immutable data. Tuples are fixed‑size, immutable sequences of heterogeneous types, written as {1, "hello", true}. Named tuples ({name: "Crystal", year: 2014}) provide field access by name. Structs can have methods and implement interfaces.

🏗️ Key Differences

  • class – reference type, heap allocated, inheritance.
  • struct – value type, no inheritance, but can include modules.
  • Tuple – immutable, positional, created with {...}.
  • NamedTuple – immutable, keys, created with {key: value}.
struct Point property x, y def initialize(@x : Int32, @y : Int32); end end p1 = Point.new(10, 20) p2 = p1 # copy (value type) tuple = {42, "answer", true} puts tuple[1] # "answer" named = {name: "Crystal", rank: 1} puts named[:name] # "Crystal" # Tuple methods puts tuple.to_a # [42, "answer", true]

🔑 Modules & Libs

🎯 Complete Definition

Modules serve as namespaces and mixins. They can contain methods, constants, and even include other modules. Crystal’s standard library provides JSON, HTTP, YAML, Regex, File, etc. External dependencies are managed via shards (Crystal's package manager) and require.

🏗️ Module Features

  • include – adds methods as instance methods (mixin)
  • extend – adds methods as class methods
  • require – loads file or library
  • import – for C bindings (similar to extern)
module Greetable def greet : String "Hello, #{name}!" end end class User include Greetable property name : String def initialize(@name); end end user = User.new("Crystalist") puts user.greet # Using standard library require "json" data = {version: "1.12", stable: true} puts data.to_json # {"version":"1.12","stable":true}

📝 Strings

🎯 Complete Definition

String is an immutable UTF‑8 encoded sequence. Strings support interpolation ("Hello #{name}"), multi‑line with """ , and a rich API for manipulation (size, split, strip, index, etc.). Character encoding is always UTF‑8; invalid byte sequences raise. Strings are comparable, and can be used as keys in hashes.

🔧 Important Methods

  • size / length – number of characters (not bytes)
  • bytesize – raw byte length
  • includes?, starts_with?, ends_with?
  • gsub, sub, scan
  • camelcase, underscore, capitalize
msg = "Crystal is awesome" puts msg.upcase # CRYSTAL IS AWESOME puts msg.includes?("awesome") # true # Interpolation year = 2014 puts "Crystal was created in #{year}." # Heredoc / multiline doc = <<-EOF Multi‑line string EOF puts doc # Char iteration msg.each_char.with_index { |c, i| puts "#{i}: #{c}" }

💾 File I/O

🎯 Complete Definition

File handling is done via the File class and IO module. Common operations: read whole file (File.read), write (File.write), open with block for auto‑close. IO also supports pipes, sockets, and memory buffers. Use File.open with mode: "r", "w", "a", "r+", etc.

📁 Modes & Methods

  • File.read(path) – returns string
  • File.write(path, content) – writes (overwrites)
  • File.open(path, "w") { |f| f.puts line }
  • Dir.entries, File.exists?, File.delete
  • IO::Memory for in‑memory string/binary I/O
require "file" # Write File.write("crystal.txt", "🔮 Crystal Pro Track\nLine2") # Read all content = File.read("crystal.txt") puts content # Read lines File.each_line("crystal.txt") do |line| puts "> #{line}" end # Append File.open("crystal.txt", "a") do |f| f.puts "Appended line" end # In‑memory IO io = IO::Memory.new io << "hello" io.rewind puts io.gets # hello

🛡️ Exceptions

🎯 Complete Definition

Exceptions are objects derived from Exception. Raised with raise, rescued with begin … rescue … else … ensure. Multiple rescue clauses can match different exception types. ensure always runs. Crystal also has try macro for nil handling.

🏗️ Structure

  • raise "message" – raises RuntimeError
  • rescue ex : ArgumentError – specific type
  • rescue – catches any Exception
  • else – runs if no exception
  • ensure – always runs
begin # risky code x = 10 / 0 rescue ex : DivisionByZeroError puts "Caught: #{ex.message}" rescue ex puts "Other error: #{ex}" else puts "No error" ensure puts "Cleanup" end # Custom exception class MyError < Exception; end def check(val) raise MyError.new("Invalid") if val < 0 end check(-5) rescue puts "rescued"

🏗️ Classes & OOP

🎯 Complete Definition

Classes are reference types supporting inheritance, polymorphism, and encapsulation. Instance variables begin with @, class variables with @@. Constructors are initialize. Getters/setters with property macro. Abstract classes and methods can be defined with abstract. Modules can be mixed in (include, extend).

🏛️ OOP Features

  • inheritance: class Child < Parent
  • abstract class / def – must be overridden
  • super – calls parent method
  • method missing via responds_to? / macro
  • virtual / polymorphic calls – automatic
abstract class Animal abstract def sound : String end class Dog < Animal def sound "woof!" end end class Cat < Animal property name : String def initialize(@name) end def sound "meow" end end animals = [Dog.new, Cat.new("Felix")] animals.each { |a| puts a.sound }

⚡ Concurrency

🎯 Complete Definition

Concurrency in Crystal uses spawn to create lightweight fibers (not OS threads). Fibers are scheduled cooperatively on a thread pool. Communication happens via channels (Channel(T)) similar to Go. The event loop (libevent) handles non‑blocking I/O. This model enables high‑concurrency servers with low overhead.

🏗️ Concurrency primitives

  • spawn – create fiber
  • Channel(T) – send/receive between fibers
  • select – wait on multiple channels
  • sleep, yield – cooperative scheduling
  • Mutex, Atomic for shared memory
channel = Channel(Int32).new spawn do puts "Fiber 1 sends 42" channel.send(42) end spawn do value = channel.receive puts "Fiber 2 received #{value}" end sleep(1) # give fibers time to run # Select example ch1 = Channel(Int32).new ch2 = Channel(String).new spawn { ch1.send(100) } spawn { ch2.send("hi") } select when val = ch1.receive puts "got int #{val}" when str = ch2.receive puts "got string #{str}" end

🔮 Macros & Metaprogramming

🎯 Complete Definition

Macros are methods that run at compile time, operating on the AST to generate code. They are defined with macro and can manipulate syntax nodes, conditionally emit code, and inspect types. Macros enable DSLs, boilerplate reduction, and performance optimisations without runtime overhead.

🔧 Macro Capabilities

  • {% if … %} / {% for … %} – compile‑time control
  • {{ expression }} – output evaluated macro value
  • \{{ run("./generator") }} – run external program
  • @type – access current type info
  • def macro – define macro method
macro define_getter(name) def {{name.id}} @{{name.id}} end end class Person def initialize(@name : String, @age : Int32) end define_getter(name) define_getter(age) end p = Person.new("Crystal", 8) puts p.name # Crystal puts p.age # 8 # Compile‑time loop macro generate_methods(names) {% for name in names %} def {{name.id}} "called {{name.id}}" end {% end %} end generate_methods([:foo, :bar]) puts foo # called foo