Sign inSign up

100hellos/sol

By 100hellos

Updated 4 months ago

"Hello World!" Written in sol

Image
0

2.1K

100hellos/sol repository overview

Hello World! From sol.

Language page — context, influences, and learning notes on the 100hellos site.

Source on GitHub (100hellos/sol)

Trying it

docker run --rm --platform="linux/amd64" 100hellos/sol:latest

Modifying it

Read the repo README, or fork the project and customize it.

For an interactive shell:

docker run --rm --platform="linux/amd64" --entrypoint="" -it 100hellos/sol:latest zsh

Sol

Sol is a compiled programming language with Ruby and Rust-inspired syntax. Memory is managed through compile-time reference counting optimized with in-place reuse.

Hello World

def main
   print("Hello World!")
end

Language Overview

Symbols
#            # comment
->           # return from function
?            # boolean method suffix (e.g. empty?, some?)
@            # field access within a class body (@name)
.field       # field access on an instance (no parens)
.method()    # method call on an instance (parens)
#{}          # string interpolation ("Hello, #{name}!")
..           # range operator (0..10)
!            # try-unwrap operator (propagates Result errors)
Variables & Type Inference
name = "Alice"          # inferred as String
age = 30                # inferred as Int
active = true           # inferred as Bool
Functions
def add(a Int, b Int) -> Int
   -> a + b
end

# Default parameter values
def greet(name String, greeting String = "Hello") -> String
   -> "#{greeting}, #{name}!"
end

-> is the return operator. Functions with no return type return Unit.

Block Syntax

Definitions use end-delimited blocks, while control flow uses braces:

def greet(name String) -> String
   -> "Hello, #{name}!"
end

if x > 0 {
   print("positive")
}
String Interpolation
name = "Sol"
print("Hello, #{name}!")
print("#{2 + 3} is five")

All types implement to_string(), which interpolation calls automatically:

arr = [1, 2, 3]
print("items: #{arr}")   # "items: [1, 2, 3]"
print(42.to_string())    # "42"
Control Flow
# If-else works as both statement and expression
result = if x > 0 { "positive" } else { "negative" }

# While loop
while i < 10 {
   i = i + 1
}

# Infinite loop with break and next (Ruby-style continue)
loop {
   if done { break }
   if skip { next }
}
Pattern Matching
match shape {
   Circle(r) => r * r * 3,
   Rect(w, h) => w * h,
   _ => 0
}

Match works as an expression and supports data extraction and wildcards. Matching on enums is exhaustive: the compiler requires all variants to be covered, or a _ wildcard to be present.

Classes
class Point
   @x Int
   @y Int

   def new(x Int, y Int)
      @x = x
      @y = y
   end

   def magnitude() -> Int
      -> @x * @x + @y * @y
   end
end

p = Point.new(3, 4)

Attribute defaults:

class Config
   @timeout Int = 30
   @retries Int = 3
end

c = Config.new()

Class methods:

class Factory
   def.class create() -> Factory
      -> Factory.new()
   end
end

Value types (stack-allocated, no reference counting). Small classes (<=16 bytes) are automatically inlined; class.inline makes it explicit:

class.inline Vec2
   @x Int
   @y Int
end
Enums
enum Shape
   Circle(radius Int)
   Rect(w Int, h Int)
   Point
end

shape = Shape.Circle(5)

Enums can carry associated data and have methods:

enum Direction
   North
   South
   East
   West

   def axis_value() -> Int
      -> match self {
         North => 10,
         South => 20,
         East => 30,
         West => 40
      }
   end
end
Option & Result
present = Option.Some(42)
absent = Option.None

value = present.unwrap_or(0)
if present.some? { ... }

# Result for error handling
def divide(a Int, b Int) -> Result<Int, String>
   if b == 0 { -> Err("division by zero") }
   -> Ok(a / b)
end
Try-Unwrap Operator

The ! operator unwraps a Result, propagating the error if it fails:

def compute() -> Result<Int, String>
   a = divide(10, 2)!
   b = divide(20, 4)!
   -> Ok(a + b)
end
Closures
double = {|x| x * 2 }
double.call(21)

# Closures as parameters
def apply(f {|Int| Int}, x Int) -> Int
   -> f.call(x)
end

apply({|n| n + 1 }, 41)

Non-local return: -> inside a closure exits the enclosing function, not just the closure, enabling early exit from iteration:

def find_first(arr Array, f {|Int| Int} -> Int) -> Int
   i = 0
   while i < arr.length() {
      f.call(arr[i])
      i = i + 1
   }
   -> 0
end

def main -> Int
   -> find_first([1, 2, 3]) { |x|
      if x == 2 { -> x }
      0
   }
end
Traits
trait Valued
   def value() -> Int
end

class Coin
   impl Valued

   @cents Int

   def value() -> Int
      -> @cents
   end
end

Traits can also include default method implementations and attribute requirements.

Bundles

Bundles compose multiple traits into a single unit:

trait Named
   def name() -> String
end

trait Valued
   def value() -> Int
end

bundle Entity
   includes Named
   includes Valued
end

class Item
   impl Entity
   # must implement both name() and value()
end
Operator Overloading
class Point
   @x Int
   @y Int

   def.op ==(other Self) -> Bool
      -> @x == other.x && @y == other.y
   end
end
Self Type

Self refers to the implementing type in traits and classes:

trait Clonable
   def clone() -> Self
end
Generics

Sol has two keywords for type parameterization:

compile defines explicit type parameters, specified at the call site:

class Box
   compile Value type

   @value Value

   def get() -> Value
      -> @value
   end
end

b = Box[Int].new(42)

Compile-time parameters can also be integers:

class Buffer
   compile size Int

   @data Int
end

infer defines associated types, inferred from usage rather than specified explicitly. Works on classes, enums, traits, and bundles:

enum Box
   infer Content

   Value(content Content)
   Empty

   def has_value? -> Int
      -> match self {
         Value(_) => 1,
         Empty => 0
      }
   end
end

container = Box.Value(42)   # Content inferred as Int
trait Container
   infer Element

   def get() -> Element
end

class Shelf
   infer Element
   impl Container

   @value Element

   def get() -> Element
      -> @value
   end
end
Type Aliases
alias Number = Int
alias Coord = Point
Modules & Imports
module Net
   class Request
      @id Int
   end

   def build(id Int) -> Request
      -> Request.new(id)
   end
end

req = Net::build(43)

Imports with aliasing, groups, and globs:

module App
   use Net::Request
   use Net::Response as Resp
   use Net::{Request, Response, ping}
   use Math::*
end

Modules can be nested and reopened. :: prefix accesses the global scope.

Directory structure maps to modules automatically. Folder names are converted to PascalCase. If a folder converts to Api but your code declares module API, the code wins:

src/
   main.sl              # root scope
   net_utils/
      client.sl         # module NetUtils
      http/
         request.sl     # module NetUtils::Http
Arrays
numbers = [1, 2, 3, 4, 5]
first = numbers[0]
slice = numbers[1..3]
numbers.push(6)
numbers.pop()
numbers.length()

# Iterators
doubled = numbers.map({|x| x * 2})
total = numbers.reduce(0, {|sum, x| sum + x})
label = numbers.join(", ")

Array type shorthand: [Int] is sugar for Array[Int].

Hash Maps
scores = Hash.new()
scores["alice"] = 95
scores["bob"] = 87

scores.get("alice")           # Option.Some(95)
scores.get_or("carol", 0)    # 0
scores.contains?("bob")      # true
scores.length()               # 2
scores.keys()                 # ["alice", "bob"]
scores.values()               # [95, 87]
scores.each({|k, v| print("#{k}: #{v}")})
Strings
text = "Hello"
text.length()
char = text[0]
sub = text[1..3]
combined = "Hello, " + "World!"

# String operations
"hello, world".split(", ")        # ["hello", "world"]
"hello".replace("l", "r")         # "herro"
"  hello  ".trim()                # "hello"
"hello".to_uppercase()            # "HELLO"
"HELLO".to_lowercase()            # "hello"
"hello".starts_with?("he")        # true
"hello".contains("ell")           # true
"hello".index_of("ll")            # Option.Some(2)
Ranges

Ranges are zero-copy unless mutated.

r = 0..10
r.length()
r.contains?(5)
r.empty?
Boolean Method Naming

Methods ending in ? return Bool:

option.some?
option.none?
result.ok?
range.empty?
string.unique?
Recursion
def factorial(n Int) -> Int
   if n <= 1 { -> 1 }
   -> n * factorial(n - 1)
end
Extern Functions (FFI)
def.extern print(str String)
Memory Model

Sol uses compile-time reference counting with copy-on-write semantics and in-place reuse. Allocations are freed deterministically. Value types (class.inline and <= 16 bytes) are stack-allocated and skip reference counting entirely.

Low-level types UnsafePointer[T] and Memory[T] are available for manual control when needed.

Tag summary

Content type

Image

Digest

sha256:880ca8038

Size

94.2 MB

Last updated

4 months ago

docker pull 100hellos/sol