Tipos
Soyuz tem um sistema de tipos estático com inferência. O compilador conhece o tipo de cada expressão em tempo de compilação, mas você raramente precisa anotar tudo.
Tipos primitivos
Section titled “Tipos primitivos”| Tipo | Descrição | Exemplos |
|---|---|---|
Int | Inteiro 64-bit | 0, 42, -7 |
Float | Ponto flutuante 64-bit | 3.14, -0.5, 1.0 |
Bool | Booleano | true, false |
String | Texto UTF-8 | "olá", "mundo" |
Char | Caractere Unicode | 'a', 'ç' |
Unit | Ausência de valor | () |
val inteiro: Int = 42val decimal: Float = 3.14val ativo: Bool = trueval texto: String = "Soyuz"val vazio: Unit = ()Strings
Section titled “Strings”Strings são delimitadas por aspas duplas e suportam interpolação com $():
val nome = "Vand"val saudacao = "Olá, $(nome)!" // "Olá, Vand!"val calculo = "Resultado: $(1 + 2 * 3)" // "Resultado: 7"Raw strings (sem processamento de escape) usam o prefixo r:
val regex = r"\d+\.\d+"val caminho = r"C:\Users\vand\docs"Tuplas
Section titled “Tuplas”Tuplas agrupam valores de tipos diferentes. São imutáveis e têm tamanho fixo.
val par: (Int, String) = (1, "um")val trio = (true, 3.14, "pi")
// Desestruturaçãoval (n, s) = parval (flag, valor, label) = trioUnit é essencialmente uma tupla vazia ().
Records
Section titled “Records”Records são estruturas de dados imutáveis e sem comportamento. São o tipo ideal para DTOs, valores de domínio e dados puros.
record Ponto { x: Float y: Float}
record Usuario { id: Int nome: String email: String}Criando records
Section titled “Criando records”val p = Ponto { x: 1.0, y: 2.0 }val u = Usuario { id: 1, nome: "Vand", email: "vand@soyuz.dev" }Acessando campos
Section titled “Acessando campos”val distX = p.x // 1.0val nome = u.nome // "Vand"Igualdade
Section titled “Igualdade”Records têm deep equality automática. Dois records são iguais se todos os seus campos são iguais:
val p1 = Ponto { x: 1.0, y: 2.0 }val p2 = Ponto { x: 1.0, y: 2.0 }
p1 == p2 // true — compara por valorRecords genéricos
Section titled “Records genéricos”record Par[A, B] { primeiro: A segundo: B}
val coords = Par[Int, Int] { primeiro: 10, segundo: 20 }Enums (Sum types / ADTs)
Section titled “Enums (Sum types / ADTs)”Enums representam um valor que pode ser uma de várias variantes. Cada variante pode carregar dados.
enum Forma { Circulo(raio: Float) Retangulo(w: Float, h: Float) Ponto}
enum Direcao { Norte Sul Leste Oeste}Criando valores de enum
Section titled “Criando valores de enum”val c = Forma.Circulo(raio: 5.0)val r = Forma.Retangulo(w: 10.0, h: 4.0)val d = Direcao.NorteUsando enums com match
Section titled “Usando enums com match”fn area(f: Forma) -> Float { return match f { Circulo(raio) => 3.14159 * raio * raio Retangulo(w, h) => w * h Ponto => 0.0 }}Enums genéricos
Section titled “Enums genéricos”enum Arvore[T] { Folha No(valor: T, esq: Arvore[T], dir: Arvore[T])}Option[T] — presença opcional
Section titled “Option[T] — presença opcional”Option[T] representa um valor que pode ou não existir. Tem duas variantes:
Some(T)— tem um valorNone— não tem valor
fn buscarPorId(id: Int) -> Option[Usuario] { // retorna Some(usuario) ou None}
val resultado = buscarPorId(42)
val nome = match resultado { Some(u) => u.nome None => "Usuário não encontrado"}Açúcar sintático com ?
Section titled “Açúcar sintático com ?”O tipo T? é equivalente a Option[T]:
fn buscar(id: Int) -> Usuario? // mesmo que Option[Usuario]
val nome: String? = NoneOptional chaining com ?.
Section titled “Optional chaining com ?.”val email = buscar(1)?.email // String? — None se buscar retornar NoneElvis operator ?:
Section titled “Elvis operator ?:”val display = buscar(1)?.nome ?: "Anônimo"Result[T] — operações que podem falhar
Section titled “Result[T] — operações que podem falhar”Result[T] representa o resultado de uma operação que pode falhar. Tem duas variantes:
Ok(T)— sucesso com valorErr(E: Error)— falha com erro
O tipo do erro é sempre algo que implementa a interface Error (da biblioteca padrão).
fn dividir(a: Int, b: Int) -> Result[Float] { if b == 0 { return Err(DivisaoErro { motivo: "divisão por zero" }) } return Ok(a.toFloat() / b.toFloat())}Tratando Result
Section titled “Tratando Result”val resultado = dividir(10, 2)Assim como em Option, o Result suporta auto-wrapping: Ok(v) pode ser apenas v e Err(e) pode ser apenas e (se e for um Error).
match resultado { Ok(v) => print("Resultado: $(v)") Err(e) => print("Erro: $(e.message())")}Encadeando com PipeQuest
Section titled “Encadeando com PipeQuest”O operador |?> curto-circuita automaticamente quando uma etapa retorna Err ou None:
val dados: Result[Dados] = buscar(1) |?> validar |?> transformar// Se qualquer etapa retornar Err ou None, o pipeline para e propaga o erro.Tipos genéricos
Section titled “Tipos genéricos”Qualquer tipo pode ser parametrizado com []:
List[Int]Map[String, Float]Option[Usuario]Result[Dados]Par[Int, String]Resumo da hierarquia de tipos
Section titled “Resumo da hierarquia de tipos”Primitivos Int, Float, Bool, String, Char, UnitCompostos record (imutável, dados puros) class (dados + comportamento) enum (sum types / ADTs)Coleções List[T], Map[K, V]Especiais Option[T] / T? (presença opcional) Result[T] (falha com motivo)Estruturais (A, B, C) (tupla) (A, B) -> C (tipo de função)