Sintaxe do Lx

Os blocos básicos da linguagem, cada conceito na sua própria seção.

Literais

Inteiros podem ser escritos em decimal, hexadecimal, octal ou binário. O prefixo indica a base.

42          # decimal
0xFF        # hexadecimal (255)
0o77        # octal (63)
0b1010      # binário (10)

Floats usam ponto. Átomos são constantes nomeadas com prefixo : e são muito usados como etiquetas.

3.14
:ok
:error
:usuario_ativo

true e false são literais próprios (não átomos). nil representa ausência.

true
false
nil

Os átomos :nil e :undefined são reservados (erro E029). Use sempre o literal nil para ausência.

Strings e charlists

Aspas duplas produzem strings binárias (UTF-8). Aspas simples produzem charlists (listas de inteiros), usadas na interoperabilidade com Erlang.

"hello"   # binary (UTF-8)
'hello'   # charlist

Interpolação avalia expressões dentro da string (sintaxe: # seguido de chaves). Escapes: \n \t \r \" \\.

nome = "Mundo"
"Olá #{nome}!"   # "Olá Mundo!"

Para juntar strings prontas use <> (concatenação). Para valores únicos, prefira interpolação.

Variáveis

Bindings imutáveis em snake_case. A atribuição é, na verdade, casamento de padrão — cada nome é vinculado uma vez por escopo.

x = 42
resultado = x * 2
{a, b} = {1, 2}    # a = 1, b = 2

Prefixar com _ ignora um valor ou silencia avisos de variável não usada.

_unused = 1
_ = fun()
{:ok, _valor} = resultado

Listas

Listas ligadas (não arrays). O operador cons [head | tail] adiciona no início em O(1).

[]
[1, 2, 3]
[0 | [1, 2, 3]]     # [0,1,2,3]
[1, 2] ++ [3, 4]    # [1,2,3,4]
[1, 2, 3] -- [2]    # [1,3]

Para processar listas, prefira as funções do módulo enum (map, filter, reduce) ou a comprehension for.

Tuplas

Coleções de tamanho fixo, ideais para agrupar valores e marcar resultados como {:ok, valor}.

{}
{1, 2}
{:ok, 42, "dados"}
point = {3, 4}

Tuplas etiquetadas são açúcar de sintaxe — e funcionam com o operador pipe.

ok{1}              # {:ok, 1}
error{"msg", 500}  # {:error, "msg", 500}
1 |> ok{}          # {:ok, 1}

Mapas

Chave-valor com chaves átomo ou expressão. O acesso em mapas comuns é por colchetes — o ponto é reservado para structs.

m = %{x: 10, y: 20}
m[:x]            # 10 (correto)
m.x              # ERRO em mapa comum (E025)

O operador spread ... mescla um mapa inteiro; a sintaxe de update muda campos específicos.

base = %{host: "localhost", port: 5432}
%{base | ...%{port: 5433, db: "app"}}
%{base | host: "db.local"}

Structs

Mapas nomeados e tipados, com campos fixos. Habilitam acesso por ponto e casamento por tipo. Literais usam %Nome{...}.

struct User {
  name :: string
  age  :: integer
}

u = %User{name: "Ada", age: 36}
u.name           # "Ada"
%{u | age: 37}   # update imutável

Structs genéricas usam parâmetros de tipo com $. Structs externas usam o prefixo do módulo.

struct Socket[$a] {
  assigns :: $a
}
s = %Socket[State]{}

%lxapp:Column{children: [...]}

Funções

def (pública), defp (privada), com tipos e múltiplas cabeças. O tipo de retorno vem depois dos parâmetros.

def add(a :: integer, b :: integer) :: integer do
  a + b
end

defp helper(x) do x * 2 end

Parâmetros default usam barra invertida dupla (\\), nunca =.

def greet(name, prefix \\ "Hello") do
  "#{prefix}, #{name}!"
end

Multi-head define várias cláusulas selecionadas por casamento, refinadas com guards (when).

def classify do
  (0)             -> :zero
  (n) when n > 0  -> :positivo
  (n)             -> :negativo
end

Lambdas

Funções anônimas. A chamada exige a sintaxe com ponto: f.(arg).

inc = fn(x) -> x + 1 end
inc.(5)          # 6

soma = fn(a, b) do a + b end
soma.(2, 3)      # 5

Lambdas podem ser passados como argumento e capturados de funções nomeadas com &nome/arity.

enum:map([1, 2, 3], fn(x) -> x * 2 end)
action = &helper/2

FFI

Emitir código Erlang nativo diretamente, com interpolação de parâmetros. Marque a função com $ depois dos parâmetros.

def write(path :: string, content :: string)$ :: :ok do
  "file:write_file(#{path}, #{content})"
end

O $ vai DEPOIS dos parâmetros: def foo(x)$ do ... end. O corpo deve ser uma única string.

Operadores

A divisão / é float; div e rem são inteiros. Strings usam <>; listas usam ++ e --.

7 / 2        # 3.5 (float)
7 div 2      # 3 (inteira)
7 rem 2      # 1 (resto)
"a" <> "b"   # "ab"
[1] ++ [2]   # [1,2]

Comparadores padrão e === (estrito). Lógicos: and/or/not e andalso/orelse (curto-circuito).

1 == 1.0     # true
1 === 1.0   # false
a and b
a orelse b

O operador in testa pertinência. O pipe |> encadeia chamadas.

3 in [1, 2, 3]   # true

[1, 2, 3]
  |> enum:map(fn(x) -> x * 2 end)
  |> enum:sum()    # 12

Tipos

Tipagem estática com inferência. type cria aliases e uniões; parâmetros de tipo usam $.

type status :: :ok | :error
type point :: {integer, integer}
type result($t) :: {:some, $t} | :none
type pair($a, $b) :: {$a, $b}

opaque esconde a definição do tipo (só o módulo o enxerga).

type opaque user_id :: integer

Nullable (?T)

?T deixa explícito que um valor pode faltar — é açúcar para T | nil.

x :: ?integer       # integer | nil

def find(list, fun) :: ?integer do ... end

Em case, valores ?T devem cobrir a ausência (exaustividade E030). O padrão ::nil casa nil e o undefined do Erlang.

case result do
  ::integer -> result
  ::nil     -> :nao_encontrado
end

Pattern matching

O coração do Lx: casar valores contra padrões. Funciona em =, case, cabeçalhos de função, with, match e receive.

{:ok, x} = {:ok, 42}      # x = 42
[a, b | _] = [1, 2, 3, 4] # a=1, b=2
{:ok, _valor} = resultado

Guards de tipo em padrões: ::type é açúcar para _ :: type.

case m do
  ::integer -> "número"
  ::string  -> "texto"
  ::nil     -> "ausente"
end

Structs casam por tipo e por campos, com guards opcionais.

case item do
  %Item{value: v} when v > 50 -> :alto
  %Item{value: v}            -> :baixo
end

If / else

Condicional simples para escolher entre dois caminhos. Para múltiplas alternativas, prefira case.

if n > 0 do
  :positivo
else
  :nao_positivo
end

Case

Casar um valor contra várias cláusulas, com guards. A estrutura de controle mais usada. Cada cláusula tem um padrão à esquerda de ->.

case valor do
  {:ok, v}      -> v
  {:error, _}   -> nil
  n when n > 0  -> :positivo
  _             -> :padrao
end

Em valores ?T, lembre de cobrir ::nil (exaustividade E030).

With

Encadear casamentos felizes em sequência, com short-circuit na primeira falha. Se um passo falha, with retorna aquele valor imediatamente.

result = with {:ok, a} <- fetch(),
               {:ok, b} <- parse(a) do
  b * 2
end

Match

Como with para um único casamento. Retorna o valor falhado em caso de incompatibilidade e aceita guard e rescue.

match {:ok, v} when v > 0 <- expr do
  v
end
match {:ok, v} <- perigoso() rescue motivo do
  {:tratado, motivo}
end

Comprehensions (for)

Map, filtro e reduce numa única expressão. O for mais simples mapeia cada elemento; when filtra.

for x in [1, 2, 3] do x * 2 end          # [2,4,6]
for x in 1..5 when x > 2 do x * 2 end    # [6,8]

Com um acumulador (acc = ...), o for vira um reduce, retornando o valor final.

for x in [1, 2, 3, 4], acc = 0 do
  acc + x
end
# 10

O corpo do for deve ser uma única expressão. Para lógica complexa, extraia uma função.

Concorrência

No alvo Erlang/OTP: processos leves e passagem de mensagens. spawn cria um processo isolado com a sua própria mailbox.

pid = spawn(fn() -> :ok end)
pid ! :hello

receive retira mensagens da mailbox por casamento, com cláusulas como case. after permite timeout.

receive do
  {:ok, data}  -> data
  {:error, _}  -> :erro
after
  5000 -> :timeout
end

Módulos e diretivas

require traz módulos ao escopo: @lx/ para a stdlib, nome para bibliotecas locais, ou caminho relativo em projetos multi-app.

require "@lx/io"
require "@lx/string"
require "cowboy"
require "../app1/helper"

defmodule cria um submódulo (gera .erl separado). as behavior declara behaviors OTP.

defmodule my_worker do
  def init(args) do {:ok, args} end
end

as behavior "gen_server"

Documentação com @moduledoc e @doc alimenta o gerador de docs e o hover do LSP.

as "application"
name :my_app
vsn "0.1.0"
mod my_app

@moduledoc "Módulo de exemplo"
@doc "Soma dois inteiros"
↑ Voltar ao topo