Recent from talks
Nothing was collected or created yet.
Crystal (programming language)
View on Wikipedia| Crystal | |
|---|---|
| Paradigm | Multi-paradigm: object-oriented, concurrent |
| Designed by | Ary Borenszweig, Juan Wajnerman, Brian Cardiff |
| Developer | Manas Technology Solutions |
| First appeared | June 19, 2014[1] |
| Stable release | 1.18.2
/ October 21, 2025 |
| Typing discipline | static, inferred, nominal, duck |
| Implementation language | Crystal |
| Platform | IA-32 (i386), x86-64, AArch64[2] |
| OS | Linux, macOS, FreeBSD, OpenBSD, Windows[2] |
| License | Apache License 2.0 |
| Filename extensions | .cr |
| Website | crystal-lang |
| Influenced by | |
| Ruby, Go[3] | |
Crystal is a high-level general-purpose, object-oriented programming language, designed and developed by Ary Borenszweig, Juan Wajnerman, Brian Cardiff and more than 400 contributors.[4] With syntax inspired by the language Ruby,[5] it is a compiled language with static type-checking, but specifying the types of variables or method arguments is generally unneeded. Types are resolved by an advanced global type inference algorithm.[6][7] Crystal is currently in active development. It is released as free and open-source software under the Apache License version 2.0.
History
[edit]Work on the language began in June 2011,[8] with the aim of merging the elegance and productivity of Ruby with the speed, efficiency, and type safety of a compiled language.[9][8] Initially named Joy, it was quickly renamed to Crystal.[8]
The Crystal compiler was first written in Ruby, but later rewritten in Crystal, thus becoming self-hosting, as of November 2013[update].[10] The first official version was released in June 2014.[11] In July 2016, Crystal joined the TIOBE index.
Description
[edit]Although resembling the Ruby language in syntax, Crystal compiles to much more efficient native code using an LLVM backend, at the cost of precluding the dynamic aspects of Ruby. The advanced global type inference used by the Crystal compiler, combined with union types, gives it more the feel of a higher-level scripting language than many other comparable programming languages. It has automated garbage collection and offers a Boehm collector. Crystal possesses a macro system and supports generics as well as method and operator overloading. Its concurrency model is inspired by communicating sequential processes (CSP) and implements lightweight fibers and channels (for interfiber communication), inspired by Go.[3]
Examples
[edit]Hello World
[edit]This is the simplest way to write the Hello World program in Crystal:
puts "Hello World!"
The same as in Ruby.
Or using an object-oriented programming style:
class Greeter
def initialize(@name : String)
end
def salute
puts "Hello #{@name}!"
end
end
g = Greeter.new("world")
g.salute
HTTP server
[edit]require "http/server"
server = HTTP::Server.new do |context|
context.response.content_type = "text/plain"
context.response.print "Hello world! The time is #{Time.local}"
end
server.bind_tcp("0.0.0.0", 8080)
puts "Listening on http://0.0.0.0:8080"
server.listen
TCP echo server
[edit]require "socket"
def handle_client(client)
message = client.gets
client.puts message
end
server = TCPServer.new("localhost", 1234)
while client = server.accept?
spawn handle_client(client)
end
Type inference and union types
[edit]The following code defines an array containing different types with no usable common ancestor. Crystal automatically creates a union type out of the types of the individual items.
desired_things = [:unicorns, "butterflies", 1_000_000]
p typeof(desired_things.first) # typeof returns the compile time type, here (Symbol | String | Int32)
p desired_things.first.class # the class method returns the runtime type, here Symbol
Concurrency
[edit]Channels can be used to communicate between fibers, which are initiated using the keyword spawn.
channel = Channel(Int32).new
spawn do
puts "Before first send"
channel.send(1)
puts "Before second send"
channel.send(2)
end
puts "Before first receive"
value = channel.receive
puts value # => 1
puts "Before second receive"
value = channel.receive
puts value # => 2
Adoption
[edit]In 2020, it was reported that the infotainment units in vehicles produced by Nikola Corporation were written in Crystal.[12] Much of the backend of the Kagi search engine is written with Crystal.[13]
Further reading
[edit]- St. Laurent, Simon; Balbaert, Ivo (February 1, 2019), Programming Crystal (P1.0 ed.), Pragmatic Bookshelf, ISBN 978-1-68050-286-2
- Dietrich, George; Bernal, Guilherme (May 27, 2022), Crystal Programming, Packt Publishing, ISBN 978-1801818674
- Wartala, Ramon (March 2016), "Die Ruby-artige Programmiersprache Crystal" [The Ruby-like programming language Crystal], Linux Magazin (in German), no. 3/2016, ISSN 1432-640X
References
[edit]- ^ "Crystal 0.1.0 released!". crystal-lang. 19 June 2014.
- ^ a b "Crystal Platform Support". crystal-lang.org.
- ^ a b "Crystal multithreading support". 23 February 2024.
- ^ "Contributors". Retrieved July 25, 2019 – via GitHub.
- ^ Borenszweig, Ary (June 16, 2016). "Crystal 0.18.0 released!". crystal-lang.org.
- ^ Brian J., Cardiff (September 9, 2013). "Type inference part 1". crystal-lang.org.
- ^ "Programming with Crystal: 'A language for humans and computers'". devm.io. July 3, 2023.
- ^ a b c David, María Inti (April 1, 2016). "The story behind #CrystalLang". manas.tech.
- ^ Hsieh, Adler (September 20, 2015). "Why Crystal programming language?". adlerhsieh.com.
- ^ Borenszweig, Ary (November 14, 2013). "Good bye Ruby Thursday". crystal-lang.org.
- ^ Borenszweig, Ary (June 19, 2014). "Crystal 0.1.0 released!". crystal-lang.org.
- ^ Pettinati, Martin (11 February 2020). "Nikola Motor Company: Crystal powered dashboards on the trucks of the future | Manas.Tech". Manas Technology Solutions.
- ^ "Zac Nowicki – Tales from Kagi | CrystalConf 2023". 13 November 2023.
External links
[edit]Crystal (programming language)
View on GrokipediaIntroduction
Overview
Crystal is a high-level, object-oriented, general-purpose programming language designed to combine the elegant syntax of Ruby with the performance benefits of static typing and compilation to native machine code via the LLVM infrastructure.[1] This approach enables developers to write concise, readable code while achieving execution speeds comparable to C.[3] The language's type system leverages advanced inference and flow typing, which tracks variable types throughout the program and prevents runtime errors like null pointer exceptions by enforcing non-nullable types where appropriate.[5][6] Crystal is distributed under the Apache License 2.0, making it free and open-source software suitable for both personal and commercial use.[1] Source files use the .cr extension, and the language emphasizes developer productivity through features like metaprogramming and a rich standard library.[7] The language supports a range of operating systems, including Linux, macOS, FreeBSD, OpenBSD, and Windows, with compatibility across architectures such as IA-32, x86-64, and AArch64.[8] Development is led by Manas Technology Solutions, and the compiler is self-hosting, meaning it is implemented in Crystal itself.[1] As of November 2025, Crystal's stable release is version 1.18.2, issued on October 21, 2025, with the project maintaining quarterly minor releases to preserve backward compatibility and introduce incremental improvements.[9]Influences and Design Philosophy
Crystal's design is primarily influenced by Ruby, from which it borrows its elegant syntax and expressive features to prioritize developer readability and productivity.[1] It also draws concurrency primitives from Go, implementing lightweight fibers and channels to facilitate efficient, non-blocking operations inspired by communicating sequential processes.[1] Additionally, Crystal incorporates static typing mechanisms akin to those in Java and performance-oriented elements from C++, including direct bindings to C libraries for low-level optimization and speed.[1] The language's goals center on delivering Ruby-like simplicity for human developers alongside compiled efficiency for computational performance, creating a balance that avoids the interpretive overhead of dynamic languages.[1] Static typing with advanced inference allows for early error detection during compilation, such as preventing null-related runtime failures, thereby enhancing reliability without sacrificing expressiveness.[1] This design enables concise code that feels dynamic while ensuring type safety and reducing boilerplate through automated inferences.[1] At its core, Crystal embodies the philosophy of "a language for humans and computers," focusing on clean, maintainable code that is intuitive to write and optimized for execution.[1] By integrating type inference to streamline development and C interoperability for performance-critical tasks, it aims to eliminate common pitfalls of dynamic languages like unexpected runtime errors.[1] Compared to Ruby, Crystal preserves much of its syntactic familiarity to ease adoption for Ruby developers, while compiling to native code to resolve Ruby's performance bottlenecks and enable faster execution.[10]History
Origins and Early Development
Crystal originated as a hobby project in June 2011, initiated by Ary Borenszweig, Juan Wajnerman, and Brian Cardiff at Manas Technology Solutions, an Argentine software consultancy.[2] The trio, all Ruby enthusiasts, sought to address key limitations in existing languages: Ruby's interpreted nature led to performance bottlenecks in production applications, while statically typed alternatives like C or Java imposed excessive verbosity that hindered developer productivity.[2] Their goal was to create a language that combined Ruby's elegant, concise syntax with the speed and type safety of compiled systems languages.[2] The project began under the name Joy but was renamed to Crystal just three days later in June 2011 to avoid naming conflicts with existing projects.[2] Development proceeded as an open-source effort on GitHub, initially hosted under the asterite organization before moving to manastech in mid-2012, where most of the prior work on the lexer and parser was retained.[2] The early compiler was implemented in Ruby to bootstrap the language, reflecting its syntactic influences.[2] Key early milestones focused on foundational components, with Borenszweig, Wajnerman, and Cardiff prioritizing the parser and type checker to enable static analysis without sacrificing Ruby-like expressiveness.[2] By November 2013, the project achieved its first self-hosting compiler, where the Crystal-written compiler successfully compiled itself, marking a significant step toward independence from Ruby.[2] The backend leveraged LLVM for generating efficient native code, aligning with the performance objectives from the outset.[2]Major Releases and Milestones
The first official release of Crystal, version 0.1.0, occurred on June 19, 2014, introducing foundational elements such as Ruby-inspired syntax, static type inference, and lightweight concurrency via fibers.[11] Early development emphasized generics for type-safe collections and data structures, with initial support present from this version onward.[12] A significant milestone came in July 2016 when Crystal entered the TIOBE Programming Community Index, reflecting its emerging popularity among developers.[13] Development toward a stable release faced challenges, including delays attributed to refinements in the type system for better inference and edge-case handling, pushing the timeline beyond initial goals set in late 2016 for a 2017 completion.[14] These efforts culminated in the stable 1.0.0 release on March 22, 2021, establishing language stability, a maintenance plan, and commitment to backward compatibility for production use.[15] The release incorporated expanded generics capabilities, enhancing flexibility for complex type parameterization beyond basic collections.[15] Post-1.0, Crystal adopted a quarterly cadence for minor releases, focusing on incremental enhancements while preserving compatibility.[4] Notable milestones include initial Windows support advancements in version 1.8.0 (April 14, 2023), with fixes for OpenSSL integration and signal handling, progressing toward full tier-1 compatibility.[16] Further Windows strides appeared in 1.9.0 (July 2023), enabling MSVC toolchain use for x64 targets, and continued in 1.11 (January 2024) with interpreter and dynamic linking improvements.[17] AArch64 architecture support, building on earlier Linux compatibility, reached maturity around 2022, facilitating native compilation on ARM64 platforms like macOS and Linux.[18] Major updates in recent years include enhanced C interoperability in 1.8.0, allowing namespaced paths in library declarations and corrected type mappings for functions like those in LibC.[16] Version 1.15.0 (January 9, 2025) introduced a new Unix event loop driver, replacing libevent for better performance, alongside multi-threading advancements via RFC integration and expanded Windows targets.[19] The most recent release, 1.18.2 on October 21, 2025, primarily addressed bug fixes, optimizations, and platform stabilizations without breaking changes.[9]Core Language Features
Syntax and Semantics
Crystal's syntax is heavily inspired by Ruby, featuring a clean, readable structure that eschews semicolons at the end of statements and relies on indentation to delineate blocks of code. Symbols are denoted by a leading colon, such as:key, and are commonly used as lightweight identifiers in hashes and method names. Hashes, which are key-value collections, can be defined using the => operator for explicit pairs (e.g., {name => "Alice"}) or shorthand notation with symbols (e.g., {name: "Alice"}), which actually produces a NamedTuple in Crystal rather than a true Hash. This design promotes concise expression without sacrificing clarity, allowing multiple statements per line separated by spaces if needed.[10]
At its core, Crystal adheres to object-oriented semantics where every value is an object, belonging to a class that defines its behavior and state. Classes support single inheritance, with a subclass deriving all instance variables, methods, and constructors from its superclass (defaulting to Reference for classes), enabling hierarchical organization of types. Modules provide mixin functionality, allowing multiple inclusion via include to incorporate instance methods or extend for class methods, effectively simulating multiple inheritance by composing behaviors across a type's ancestor chain. Method overriding is straightforward, where a subclass redefines a superclass method, optionally invoking the parent version with super to extend or modify its logic.[20][21][22]
Control structures in Crystal mirror Ruby's expressive style, facilitating conditional and iterative logic. The if/else construct evaluates a condition and executes the corresponding block, with elsif for chained checks and unless as its negation; expressions like if can also serve as modifiers post-statement. The case expression acts as an enhanced switch, matching a value against branches with semantic flexibility beyond exact equality, such as type checks or ranges. Loops include while and until, which repeat a block while (or until) a condition holds, respectively, with next to skip iterations and break to exit early. For collection processing, iterators like each and map invoke blocks on elements, promoting functional-style operations over imperative loops.[23][24][25]
Macros enable powerful metaprogramming through compile-time code generation, akin to Lisp's macro system, where code is treated as data via abstract syntax trees (ASTs). Defined with the macro keyword, they interpolate AST nodes using {{ }} and support control flow like {% if %} and {% for %}, allowing dynamic generation of methods, classes, or other constructs based on type information or arguments. This facility supports advanced features like domain-specific languages or boilerplate reduction, with the generated code undergoing full type checking post-expansion.[26]
Error handling relies on exceptions, raised via the top-level raise method with a message or custom Exception subclass, and caught using the begin/[rescue](/page/Rescue)/[ensure](/page/Ensure) construct. The [rescue](/page/Rescue) clause handles specific exception types (e.g., rescue ex : IndexError) or any via a bare [rescue](/page/Rescue), while [ensure](/page/Ensure) guarantees execution for cleanup regardless of exceptions; an optional else runs only if no exception occurs. This approach integrates seamlessly with Crystal's type system, where exceptions propagate up the call stack until rescued.[27]
Type System
Crystal features a static type system that performs type checking at compile time, ensuring type safety while allowing for Ruby-like expressiveness through advanced inference mechanisms. This system is primarily nominal, where types are identified by their names and inheritance relationships, but incorporates elements of structural compatibility through union types and method dispatch, enabling a hybrid approach that feels dynamic in usage yet enforces safety statically. The compiler catches type errors early, contributing to runtime performance by generating optimized native code without the overhead of dynamic type checks.[28] Type inference in Crystal automatically deduces types from contextual information, such as variable initializations, method calls, and control flow, without requiring explicit annotations in most cases. The compiler applies syntactic rules to build a set of possible types, resulting in a union type if multiple possibilities arise, and includes the Nil type for uninitialized or conditionally absent values. This inference applies to local variables, method return types, and generic parameters, promoting concise code while maintaining compile-time verification; explicit type restrictions can be added for clarity or to resolve ambiguities.[5] Union types allow a single variable or expression to represent multiple possible types, such as Int32 | String, which the compiler infers automatically when assignments or branches introduce type variability. Operations on union-typed values require methods to be defined across all constituent types, with the compiler enforcing exhaustive compatibility to prevent runtime errors; virtual types may be generated for unions within the same inheritance hierarchy to optimize checking. This mechanism supports flexible data handling while ensuring safety through compile-time analysis.[29] Generics enable the creation of parameterized types, such as Array(T) or Hash(K, V), where T, K, and V are type variables that get instantiated with concrete types at compile time. Classes, structs, and modules can declare one or more type parameters, supporting polymorphism for collections and data structures; multiple parameters and variable arguments via splats are also allowed. Inheritance in generics follows nominal rules, with subclasses specifying concrete instances or delegating parameters from superclasses.[12] Crystal avoids implicit nulls by using an explicit Nil type, which represents the absence of a value and must be handled deliberately to prevent exceptions. Flow typing tracks non-nil paths in control structures like if var.nil?, narrowing the type to exclude Nil in the non-nil branch for local variables, thus enabling safe access without casts in verified contexts. This explicit approach, combined with union types including Nil, enforces null safety at compile time while allowing developers to model optional values precisely.[30][31] Method overloading is resolved at compile time based on parameter types, counts, named arguments, and block presence, allowing multiple definitions of the same method name with differing signatures. The compiler selects the most restrictive matching overload, requiring less restrictive versions to be defined later; this type-based dispatch enhances code reuse without runtime overhead. Regarding variance, Crystal supports covariance in certain generic types like Array, where a subtype array can be treated as the supertype (e.g., Array(Bar) assignable to Array(Foo) if Bar inherits from Foo), but lacks full contravariance support, adhering to nominal inheritance rules for safety.[32][21]Memory Management and Performance
Crystal employs automatic memory management through the Boehm-Demers-Weiser conservative garbage collector, a mark-and-sweep algorithm that eliminates the need for manual allocation and deallocation.[33][34] This approach ensures safe handling of heap memory while integrating seamlessly with Crystal's type system, where all allocations useGC_malloc and the collector manages reclamation without developer intervention.[34] The Boehm GC supports configuration tweaks to balance collection frequency and pause durations, contributing to responsive runtime behavior in typical applications.[34]
Crystal achieves high runtime efficiency through ahead-of-time compilation to native machine code via the LLVM backend, bypassing virtual machine overhead and enabling speeds comparable to C.[35][36] This process leverages LLVM's optimization passes, including inlining and devirtualization facilitated by Crystal's static typing, to produce efficient executables.[35] As a result, Crystal programs often run 10 to 100 times faster than equivalent Ruby implementations, depending on the workload, due to the absence of interpretation or just-in-time compilation costs.[37][38]
For interoperability and further performance gains, Crystal provides seamless bindings to C libraries via its foreign function interface (FFI), allowing direct calls to native code without wrappers or runtime penalties.[39] Features like out parameters and to_unsafe simplify integration, enabling developers to leverage optimized C ecosystems—such as for I/O or cryptography—while maintaining Crystal's syntax and safety.[39] In benchmarks involving C bindings, Crystal performs nearly identically to native C, with execution times differing by less than 1%.[40]
Performance evaluations demonstrate Crystal's competitiveness in practical scenarios, often matching or approaching C and Go in efficiency. For instance, in file I/O tasks like writing lines to disk, Crystal completes operations in about 1.2 seconds, closely trailing C (2.5 seconds) and Go (1.2 seconds) while outperforming Ruby by over 10 times.[40] In network-related benchmarks such as TCP socket handling—relevant to web tasks—Crystal achieves 0.94 seconds, comparable to Go (0.91 seconds) and C (0.84 seconds), highlighting its suitability for high-throughput server applications.[40] Overall, these results underscore Crystal's balance of Ruby-like development speed with low-level performance.[40]
Concurrency Model
Crystal's concurrency model is built around fibers and channels, providing a lightweight, cooperative approach to concurrent programming that emphasizes safety and efficiency without relying on shared mutable state. This model draws inspiration from Communicating Sequential Processes (CSP) and is designed for single-threaded execution by default, where all concurrency occurs within a single operating system thread managed by the Crystal runtime.[41][41] Fibers serve as the core unit of concurrency in Crystal, functioning as lightweight, user-space threads or coroutines that enable cooperative multitasking. Unlike operating system threads, which incur significant overhead due to kernel involvement, fibers are managed entirely by the runtime and require far less memory—starting with a 4KB stack that can grow up to 8MB as needed—allowing potentially millions to run concurrently on 64-bit systems. This design makes fibers cheaper and more scalable for I/O-bound tasks, as they yield control explicitly rather than being preempted by the OS scheduler.[41][42] Communication between fibers occurs primarily through channels, which are typed constructs (parameterized as Channel(T)) that support both buffered and unbuffered modes for passing data without shared memory access. Inspired by Go's channels, Crystal's implementation handles synchronization internally, blocking senders and receivers as appropriate to ensure orderly data flow and avoid locks or semaphores. Buffered channels can hold a specified number of elements before blocking, while unbuffered ones synchronize directly between producer and consumer fibers. Key primitives include spawn for creating new fibers, select for multiplexing operations across multiple channels (waiting for the first to become ready), and atomic operations via the Atomic(T) class for safe updates in scenarios involving potential parallelism.[41][43][44] The runtime scheduler orchestrates fiber execution using an event-loop mechanism that directly integrates with system selectors like epoll and kqueue, integrating non-blocking I/O operations such as file descriptors, timers, and network events to prevent blocking the entire program. This allows the scheduler to efficiently resume ready fibers from a queue while delegating asynchronous tasks, promoting high throughput for concurrent I/O without the complexity of thread pools. By default, this single-threaded model eliminates data races, as all fibers share the same thread and mutable state is confined to non-concurrent access patterns; channels further enforce this by prohibiting unsafe sharing. Multi-threading, introduced experimentally in 2019 and ongoing improvements as of 2025, enables true parallelism via flags like -Dpreview_mt but remains unstable and requires careful use of atomics to avoid races. As of 2025, multi-threading support continues to advance through a partnership with 84codes, with improvements in execution contexts and memory management in version 1.18.[41][41][45][46][47]Standard Library and Ecosystem
Standard Library Components
Crystal's standard library offers a rich collection of modules designed to handle fundamental programming needs, enabling developers to build applications without external dependencies for many common operations.[48] It emphasizes type safety through integration with the language's static type system, ensuring compile-time checks for operations on built-in types. Core data structures are provided by modules such as Array, which implements dynamic arrays with methods for appending, slicing, and iterating elements, and Hash, a key-value store supporting generic types for keys and values. String handling is covered by the String class, featuring methods for concatenation, substitution, encoding conversions, and pattern matching via regular expressions. For temporal data, the Time module manages timestamps, durations, and formatting, while Date focuses on calendar dates and arithmetic. File input/output is facilitated by the File class for reading, writing, and manipulating files, alongside the IO abstract base for streams and pipes. Networking capabilities include the HTTP module with HTTP::Client for making requests and HTTP::Server for handling incoming connections, both supporting protocols like HTTPS via built-in TLS. Data serialization is addressed by JSON for parsing and generating JSON documents with type-safe builders, and XML for XML document manipulation using a DOM-like API. Low-level networking uses TCPSocket and related classes for TCP connections, UDP via UDPSocket, and general socket operations. Utility modules encompass Math for trigonometric, logarithmic, and statistical functions; Random for pseudo-random number generation with secure options; Log for configurable logging with levels and handlers; and Process for spawning child processes, environment access, and signal handling. The library incorporates C bindings for performance-critical components, including OpenSSL for cryptography operations like hashing and encryption, and zlib for compression and decompression tasks. Recent expansions in versions from 2024 to 2025 have enhanced asynchronous I/O through scheduler and event loop optimizations, alongside improved Unicode support updated to version 17.0 for better internationalization in string and text processing.[46] These updates maintain backward compatibility while adding features like refined Time inspection formats and improved YAML alias resolution in the YAML module.[46]Package Management and Tools
Crystal's package management is handled by Shards, a built-in dependency manager introduced in 2016 that facilitates the declaration, resolution, and installation of libraries for projects and applications. Shards uses a YAML-based manifest file namedshard.yml to specify dependencies, including their names, versions, and sources, typically hosted on GitHub repositories.[49] It supports semantic versioning to resolve compatible dependency graphs and generates a shard.lock file to lock exact versions and commits, ensuring reproducible builds across environments; this lockfile is committed for applications but often ignored for libraries to allow flexible updates.[49] The standard library provides foundational support for Shards operations, such as HTTP fetching for remote dependencies.[49]
For building and testing, the Crystal compiler includes crystal build, which compiles source code into standalone executables, optimizing for performance with options like release mode for production.[50] Integrated with Shards, the command shards build automatically installs dependencies before compilation.[49] Testing is supported via the built-in crystal spec runner, which executes specifications written using the Spec module's domain-specific language for describing expected behaviors, such as equality checks and error raising, with files conventionally placed in a spec/ directory ending in _spec.cr.[51]
Additional development tools enhance productivity and code quality. The crystal tool context command provides ambient context for type resolution at specific code locations, displaying inferred types and variable information to aid debugging and understanding without runtime execution.[50] Code formatting is handled by crystal tool format, which applies consistent styling rules to source files, supporting options like check mode for CI integration without modifying files.[50] Documentation generation occurs via crystal docs, which extracts inline comments preceding public API elements to produce a browsable website, excluding private features unless explicitly marked.[52]
The Crystal ecosystem has grown significantly, with over 10,000 shards available on shards.info as of November 2025, many integrated with GitHub for version control and distribution.[53] Community contributions via GitHub repositories form the backbone of this expansion, enabling easy discovery and forking of packages. For enterprise use, Manas Technology Solutions offers Crystal Compass, a support service providing expert reviews, architectural guidance, and direct assistance from the language's core developers.[54]
Practical Examples
Basic Syntax and Hello World
Crystal programs begin execution from the top-level code in the source file, with no explicit main method required; the compiler treats the entire file as the entry point. For script-like usage, Crystal supports shebangs, allowing files to start with a line such as#!/usr/bin/env crystal to enable direct execution after making the file executable.[55]
A basic "Hello World" program in Crystal demonstrates this simplicity:
puts "Hello World"
puts "Hello World"
puts method from the standard library to output the string literal "Hello World" followed by a newline to standard output. The puts syntax is inspired by Ruby, and Crystal's type inference automatically treats the argument as a String.[56]
To run the program saved as hello.cr, use the interpreter mode with crystal run hello.cr, which compiles and executes in one step, producing the output "Hello World". For a standalone binary, compile it using crystal build hello.cr, which generates an executable file named hello that can be run directly without the Crystal toolchain.[56]
Type Inference and Unions
Crystal's type inference system allows developers to omit explicit type annotations in many cases, enabling Ruby-like conciseness while maintaining static type safety. The compiler infers types for local variables, instance variables, and method returns based on syntactic rules, such as literal assignments and method calls, forming unions when multiple types are possible across control flow paths.[5] This reduces boilerplate and promotes readable code without sacrificing compile-time error detection.[5] For instance, consider a simple variable assignment and arithmetic operation:x = 1
x += 1
puts typeof(x) # Outputs: Int32
x = 1
x += 1
puts typeof(x) # Outputs: Int32
1 infers Int32 for x, as integers default to 32-bit signed integers unless specified otherwise. The subsequent addition reinforces this type, and typeof(x) confirms the inferred type at compile time. This inference applies to local variables without explicit declarations, preventing type mismatches early in development.[5][57]
Union types extend this flexibility by allowing a single variable or parameter to hold values of multiple disparate types, denoted by the pipe operator |. For example, a function can accept either an integer or a string:
def foo(x : Int32 | String)
case x
when Int32
puts "Number: #{x}"
when String
puts "Text: #{x}"
end
end
foo(42) # Outputs: Number: 42
foo("hello") # Outputs: Text: hello
def foo(x : Int32 | String)
case x
when Int32
puts "Number: #{x}"
when String
puts "Text: #{x}"
end
end
foo(42) # Outputs: Number: 42
foo("hello") # Outputs: Text: hello
case statement provides exhaustive pattern matching over the union, ensuring all possibilities are handled; omitting a branch for String would trigger a compile-time error. This enforces completeness and catches incomplete logic at build time.[29]
Unions often include Nil to represent optional values, such as uninitialized variables or conditional assignments:
def process_value(input : Int32 | Nil)
case input
when Int32
input * 2
when Nil
0
end
end
result = process_value(5) # Int32
result = process_value(nil) # 0 (Int32)
def process_value(input : Int32 | Nil)
case input
when Int32
input * 2
when Nil
0
end
end
result = process_value(5) # Int32
result = process_value(nil) # 0 (Int32)
Nil without checking, the compiler would reject it, as Nil lacks such methods. This nil-safety in unions prevents runtime nil errors by verifying type compatibility across all branches.[29]
Overall, type inference and unions minimize explicit annotations—often eliminating them entirely for simple cases—while enabling the compiler to detect type errors, such as invalid method calls on unions, before execution. This balance yields performant, safe code with less verbosity than fully annotated languages. Crystal also supports flow typing, narrowing unions (e.g., excluding Nil after a non-nil check) within scopes for refined type handling.[5][29]
Concurrency and Fibers
Crystal's concurrency model relies on fibers, which are lightweight, cooperatively scheduled execution units managed by the runtime, enabling efficient parallelism without the overhead of operating system threads. By default, Crystal programs execute in a single thread using an event loop that switches between fibers during I/O operations or explicit yields, ensuring non-blocking behavior for tasks like network requests. This approach allows for thousands of concurrent fibers, far exceeding typical thread limits, while maintaining Ruby-like simplicity in syntax.[58] Fibers are created using thespawn keyword, which starts a new fiber without blocking the caller. For instance, the following code spawns a fiber that sleeps for one second before printing a message, demonstrating lightweight parallelism:
spawn do
[sleep](/page/Sleep) 1.second
puts "done"
end
[sleep](/page/Sleep) 2.seconds # Allow time for the fiber to complete
spawn do
[sleep](/page/Sleep) 1.second
puts "done"
end
[sleep](/page/Sleep) 2.seconds # Allow time for the fiber to complete
WaitGroup class from the standard library, which tracks completion via a counter. Each spawned fiber calls done upon finishing, and the main fiber waits until all are complete:
require "wait_group"
wg = WaitGroup.new(3)
3.times do
wg.spawn do
sleep 1.second
puts "Fiber completed"
end
end
wg.wait
puts "All fibers done"
require "wait_group"
wg = WaitGroup.new(3)
3.times do
wg.spawn do
sleep 1.second
puts "Fiber completed"
end
end
wg.wait
puts "All fibers done"
ch = Channel(String).new
spawn do
ch.send "hi"
end
puts ch.receive # Outputs: hi
ch = Channel(String).new
spawn do
ch.send "hi"
end
puts ch.receive # Outputs: hi
ch = Channel(Int32).new
# Producer fiber
spawn do
5.times do |i|
sleep 0.5.seconds
ch.send i
end
ch.close
end
# Consumer fiber (main)
5.times do
value = ch.receive
puts "Received: #{value}"
end
ch = Channel(Int32).new
# Producer fiber
spawn do
5.times do |i|
sleep 0.5.seconds
ch.send i
end
ch.close
end
# Consumer fiber (main)
5.times do
value = ch.receive
puts "Received: #{value}"
end
select statement waits on the first ready operation, enabling efficient multiplexing similar to other concurrent languages. The following example uses select to read from the first available channel:
ch1 = Channel(Int32).new
ch2 = Channel(Int32).new
spawn do
sleep 1.second
ch1.send 42
end
spawn do
sleep 0.5.seconds
ch2.send 99
end
select
when value = ch1.receive
when value = ch2.receive
end
puts "Received from ready channel: #{value}" # Outputs: 99 (from ch2 first)
ch1 = Channel(Int32).new
ch2 = Channel(Int32).new
spawn do
sleep 1.second
ch1.send 42
end
spawn do
sleep 0.5.seconds
ch2.send 99
end
select
when value = ch1.receive
when value = ch2.receive
end
puts "Received from ready channel: #{value}" # Outputs: 99 (from ch2 first)
Web and Network Programming
Crystal's standard library provides robust support for web and network programming through modules likeHTTP and Socket, enabling developers to build efficient servers without external dependencies. The HTTP::Server class facilitates the creation of concurrent HTTP servers that handle requests in separate fibers, ensuring non-blocking I/O operations for high performance. Similarly, the Socket module allows for low-level TCP server implementation, where fibers can be spawned to manage multiple client connections concurrently.[61][62]
To illustrate HTTP server capabilities, consider a basic server using the HTTP module. This example sets up a server that responds to requests on port 8080 with a simple message, demonstrating binding and request handling.
require "http/server"
server = HTTP::Server.new do |context|
context.response.content_type = "text/plain"
context.response.print "Hello World!"
end
address = server.bind_tcp 8080
puts "Listening on http://#{address}"
server.listen
require "http/server"
server = HTTP::Server.new do |context|
context.response.content_type = "text/plain"
context.response.print "Hello World!"
end
address = server.bind_tcp 8080
puts "Listening on http://#{address}"
server.listen
HTTP::Server.new initializes the server with a block that receives an HTTP::Server::Context object for each incoming request. The bind_tcp method binds the server to the specified port, returning the listening address, and listen starts the event loop to accept connections. Internally, each request is processed in a dedicated fiber, allowing the server to handle multiple concurrent requests without blocking. To run this, compile and execute with crystal run server.cr, which starts the server immediately and responds to HTTP GET requests at http://localhost:8080. For production, use crystal build server.cr --release to optimize performance.[63][61]
For more advanced web programming, routing can be implemented by inspecting the request path within the handler block, mimicking lightweight frameworks. Here's an example with path-based routing and a JSON response:
require "http/server"
require "json"
server = HTTP::Server.new do |context|
case context.request.path
when "/"
context.response.content_type = "text/plain"
context.response.print "Home page"
when "/api/users"
context.response.content_type = "application/json"
users = [{"id" => 1, "name" => "Alice"}, {"id" => 2, "name" => "Bob"}]
context.response.print users.to_json
else
context.response.status_code = 404
context.response.print "Not Found"
end
end
address = server.bind_tcp 8080
puts "Listening on http://#{address}"
server.listen
require "http/server"
require "json"
server = HTTP::Server.new do |context|
case context.request.path
when "/"
context.response.content_type = "text/plain"
context.response.print "Home page"
when "/api/users"
context.response.content_type = "application/json"
users = [{"id" => 1, "name" => "Alice"}, {"id" => 2, "name" => "Bob"}]
context.response.print users.to_json
else
context.response.status_code = 404
context.response.print "Not Found"
end
end
address = server.bind_tcp 8080
puts "Listening on http://#{address}"
server.listen
case statement routes based on context.request.path, supporting different endpoints. For the /api/users route, the JSON module serializes a Crystal array of hashes to JSON format using to_json, setting the appropriate content type for structured data exchange. This approach leverages Crystal's type-safe JSON handling while keeping the server lightweight and fiber-based for concurrency.[61]
Network programming extends to TCP servers via the TCPServer class, useful for custom protocols like echo services. The following example creates an echo server that reads from clients and echoes back messages, using fibers for non-blocking handling:
require "socket"
def handle_client(client : TCPSocket)
loop do
if message = client.gets
client.puts "Echo: #{message}"
else
break
end
end
client.close
end
server = TCPServer.new("[localhost](/page/Localhost)", 1234)
puts "Listening on localhost:1234"
loop do
if client = server.accept?
spawn handle_client(client)
end
end
require "socket"
def handle_client(client : TCPSocket)
loop do
if message = client.gets
client.puts "Echo: #{message}"
else
break
end
end
client.close
end
server = TCPServer.new("[localhost](/page/Localhost)", 1234)
puts "Listening on localhost:1234"
loop do
if client = server.accept?
spawn handle_client(client)
end
end
TCPServer.new binds to the host and port, and the loop with accept? awaits incoming connections, yielding a TCPSocket for each client. The spawn keyword creates a new fiber to run handle_client, where gets reads lines non-blockingly and puts writes the echoed response. This fiber-per-client model ensures the accept loop remains responsive, enabling the server to handle multiple simultaneous connections efficiently. Run it with crystal run echo_server.cr, and test by connecting via telnet localhost 1234.[62][64]
These standard library features form the foundation for web and network applications in Crystal, with frameworks like Kemal or Lucky—managed via the Shards package system—building upon them for more complex routing and middleware.[65]