Skip to content

Latest commit

 

History

History
284 lines (227 loc) · 5.17 KB

File metadata and controls

284 lines (227 loc) · 5.17 KB

Advanced Patterns

Trait Implementations

import std.string (ToString)

type pub Person {
  let @name: String
}

impl ToString for Person {
  fn pub to_string -> String {
    @name
  }
}

Key points:

  • Use impl TraitName for TypeName syntax
  • Traits define interfaces that types can implement
  • Standard library traits like ToString are commonly used

Enum with Trait Implementation

type pub enum ErrorKind {
  case InvalidHeader
  case InvalidXRef
  case MissingObject
}

impl ToString for ErrorKind {
  fn pub to_string -> String {
    match self {
      case InvalidHeader -> 'Invalid PDF header'
      case InvalidXRef -> 'Invalid cross-reference table'
      case MissingObject -> 'Missing object'
    }
  }
}

Key points:

  • Enums can implement traits
  • Use match self inside trait implementations
  • Single-quoted strings work fine for string literals

Iterator Methods

# find_map: Find first value that maps to Some
let result = entries.iter.find_map(fn (entry) {
  if entry.key == target {
    Option.Some(ref entry.value)
  } else {
    Option.None
  }
})

# map: Transform each element
let items = arr.iter.map(fn (obj) { obj.to_string })

# join: Join iterator results with separator
let result = StdString.join(items, with: ' ')

Key points:

  • .iter returns an iterator over collections
  • find_map combines find and map operations
  • Iterator methods work well with closures

Option Return Patterns

# Pattern match on nested Option
fn pub get_name(key: String) -> Option[String] {
  match get(key) {
    case Some(v) -> {
      match v {
        case Name(name) -> Option.Some(name.clone)
        case _ -> Option.None
      }
    }
    case _ -> Option.None
  }
}

Key points:

  • Use nested pattern matching for complex Option/Result chains
  • Return Option.Some(value) not just value
  • Use .clone() when returning owned copies

Generic Functions with Trait Bounds

import std.bytes (Bytes, ToSlice)

# Generic function that accepts String, ByteArray, or their Slices
fn example[B: Bytes, S: ToSlice[B]](value: ref S) -> String {
  value.to_slice.slice(0, 5).to_string
}

Static Factory Methods Pattern

type pub Config {
  let @max_errors: Int
  let @strict_mode: Bool

  fn pub static default -> Config {
    Config(max_errors: 100, strict_mode: false)
  }

  fn pub static strict -> Config {
    Config(max_errors: 0, strict_mode: true)
  }

  fn pub static permissive -> Config {
    Config(max_errors: 9999999, strict_mode: false)
  }
}

# Usage
let config = Config.default
let strict_config = Config.strict

Builder Pattern

type pub RequestBuilder {
  let mut @method: String
  let mut @path: String
  let mut @headers: Array[(String, String)]
}

impl RequestBuilder {
  fn pub static new -> RequestBuilder {
    RequestBuilder(
      method: "GET",
      path: "/",
      headers: [],
    )
  }

  fn pub mut method(method: String) -> mut RequestBuilder {
    @method = method
    self
  }

  fn pub mut path(path: String) -> mut RequestBuilder {
    @path = path
    self
  }

  fn pub mut header(name: String, value: String) -> mut RequestBuilder {
    @headers.push((name, value))
    self
  }

  fn pub build -> Request {
    Request(
      method: @method,
      path: @path,
      headers: @headers,
    )
  }
}

# Usage
let request = RequestBuilder.new
  .method("POST")
  .path("/api/users")
  .header("Content-Type", "application/json")
  .build

Error Type Pattern

type pub enum ParseError {
  case InvalidSyntax(String)
  case UnexpectedToken(String)
  case EndOfInput
}

impl ToString for ParseError {
  fn pub to_string -> String {
    match self {
      case InvalidSyntax(msg) -> "Invalid syntax: ${msg}"
      case UnexpectedToken(token) -> "Unexpected token: ${token}"
      case EndOfInput -> "Unexpected end of input"
    }
  }
}

Process Communication Pattern

import std.sync (Promise)

type async Worker {
  let @id: Int

  fn async pub compute(data: String, promise: uni Promise[String]) {
    let result = process_data(data)
    promise.set(result)
  }
}

type async Main {
  fn async main {
    let worker = Worker(id: 1)
    let result = await worker.compute("input data")
    # Use result
  }
}

Resource Management Pattern

type pub Connection {
  let @handle: Int
  let mut @closed: Bool
}

impl Connection {
  fn pub static open(path: String) -> Result[Connection, String] {
    # Open connection
    Result.Ok(Connection(handle: 1, closed: false))
  }

  fn pub mut close {
    if not @closed {
      # Close the resource
      @closed = true
    }
  }
}

impl Drop for Connection {
  fn mut drop {
    close
  }
}

Recursive Type Pattern

type pub enum JsonValue {
  case Null
  case Bool(Bool)
  case Number(Float)
  case String(String)
  case Array(Array[JsonValue])
  case Object(Array[(String, JsonValue)])
}

When to Use Constants

Use constants for:

  • Configuration values (ports, timeouts, buffer sizes)
  • Version numbers or API identifiers
  • Magic numbers that need meaningful names
  • Application-wide settings that never change
let PORT = 3000
let DEFAULT_TIMEOUT = 30
let MAX_RETRIES = 3
let API_VERSION = "v1"