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_ativotrue e false são literais próprios (não átomos). nil representa ausência.
true
false
nilOs á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' # charlistInterpolaçã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 = 2Prefixar com _ ignora um valor ou silencia avisos de variável não usada.
_unused = 1
_ = fun()
{:ok, _valor} = resultadoListas
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ávelStructs 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 endParâmetros default usam barra invertida dupla (\\), nunca =.
def greet(name, prefix \\ "Hello") do
"#{prefix}, #{name}!"
endMulti-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
endLambdas
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) # 5Lambdas 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/2FFI
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})"
endO $ 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 bO 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() # 12Tipos
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 :: integerNullable (?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 ... endEm 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
endPattern 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} = resultadoGuards de tipo em padrões: ::type é açúcar para _ :: type.
case m do
::integer -> "número"
::string -> "texto"
::nil -> "ausente"
endStructs casam por tipo e por campos, com guards opcionais.
case item do
%Item{value: v} when v > 50 -> :alto
%Item{value: v} -> :baixo
endIf / else
Condicional simples para escolher entre dois caminhos. Para múltiplas alternativas, prefira case.
if n > 0 do
:positivo
else
:nao_positivo
endCase
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
endEm 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
endMatch
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
endmatch {:ok, v} <- perigoso() rescue motivo do
{:tratado, motivo}
endComprehensions (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
# 10O 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 ! :helloreceive retira mensagens da mailbox por casamento, com cláusulas como case. after permite timeout.
receive do
{:ok, data} -> data
{:error, _} -> :erro
after
5000 -> :timeout
endMó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