💻 Exemplos: Paradigma Funcional e Imutabilidade
Módulo 10: Paradigmas & Padrões • Bloco 1 • Demonstrações Práticas
🔄 Fio Condutor - Fase 3 (ver Exemplos do Cap 01 e Exemplos do Cap 02): O Carrinho de Compras implementado sob o rigor do paradigma funcional. Aqui, nenhuma variável sofre reatribuição, os dados são registros congelados (frozen), os cálculos fluem através de funções puras de alta ordem (
map,filter,reduce), e o tratamento de falhas adota tipos monádicos sem lançamento de exceções.
1. Carrinho de Compras Imutável com Composição e HOFs
Neste exemplo, o carrinho de compras é composto por estruturas 100% imutáveis (NamedTuple). O pipeline de liquidação utiliza composição funcional pura (pipe), currying e funções de alta ordem, garantindo que o estado original nunca seja corrompido:
# carrinho_funcional_puro.py
from typing import NamedTuple, Tuple, Callable, Any
from functools import reduce
# 1. Estruturas de Dados Puras e Imutáveis
class Item(NamedTuple):
sku: str
nome: str
preco: float
quantidade: int
categoria: str
class Carrinho(NamedTuple):
id_carrinho: str
itens: Tuple[Item, ...]
cupom: str = ""
# 2. Primitiva Funcional: Operador Pipe para Composição Algébrica (f ∘ g ∘ h)
def pipe(dado_inicial: Any, *funcoes: Callable[[Any], Any]) -> Any:
"""Aplica sequencialmente uma cadeia de funções puras a um dado inicial."""
return reduce(lambda acc, fn: fn(acc), funcoes, dado_inicial)
# 3. Funções Puras de Transformação e Consulta
def calcular_subtotal_item(item: Item) -> float:
"""Calcula o subtotal de um único item sem efeitos colaterais."""
return item.preco * item.quantidade
def calcular_subtotal_carrinho(carrinho: Carrinho) -> float:
"""Calcula a soma de todos os subtotais através de map e reduce puros."""
return reduce(
lambda acc, valor: acc + valor,
map(calcular_subtotal_item, carrinho.itens),
0.0
)
def filtrar_itens_promocao(carrinho: Carrinho) -> Tuple[Item, ...]:
"""Retorna apenas itens elegíveis a desconto promocional (qtd >= 2)."""
return tuple(filter(lambda item: item.quantidade >= 2, carrinho.itens))
# 4. Currying e Aplicação Parcial de Descontos e Fretes
def criar_aplicador_desconto(percentual: float) -> Callable[[float], float]:
"""Retorna uma função especializada que aplica um desconto percentual fixo."""
fator = (100.0 - percentual) / 100.0
return lambda total: total * fator
def criar_calculadora_frete(custo_base: float, limiar_frete_gratis: float) -> Callable[[float], float]:
"""Retorna uma função que calcula o frete com base no valor total do carrinho."""
return lambda total: 0.0 if total >= limiar_frete_gratis else custo_base
def criar_aplicador_imposto(aliquota_icms: float) -> Callable[[float], float]:
"""Calcula o valor do imposto estadual sobre o faturamento."""
return lambda total: total * (1.0 + aliquota_icms)
# 5. Execução e Demonstração de Imutabilidade
if __name__ == "__main__":
itens_iniciais = (
Item("SKU-1001", "Mouse Gamer 16000 DPI", 150.0, 1, "PERIFERICOS"),
Item("SKU-1002", "Teclado Mecânico RGB", 350.0, 2, "PERIFERICOS"),
Item("SKU-1003", "Cabo HDMI 2.1 Trançado", 45.0, 3, "CABOS"),
Item("SKU-1004", "Mousepad Speed Extra Grande", 60.0, 1, "ACESSORIOS"),
)
carrinho_original = Carrinho("CART-BR-2026", itens_iniciais, cupom="PROMO15")
# Cálculo de Subtotais com HOFs
subtotal_bruto = calcular_subtotal_carrinho(carrinho_original)
itens_em_promocao = filtrar_itens_promocao(carrinho_original)
# Criar funções especializadas via currying
desconto_cupom = criar_aplicador_desconto(15.0) # 15% de desconto
calculadora_frete = criar_calculadora_frete(45.0, 800.0) # Frete R$45; grátis acima de R$800
imposto_estadual = criar_aplicador_imposto(0.08) # 8% ICMS
# Pipeline Funcional de Liquidação Financeira
total_com_desconto = desconto_cupom(subtotal_bruto)
frete_aplicado = calculadora_frete(total_com_desconto)
valor_tributado = imposto_estadual(total_com_desconto)
valor_final = valor_tributado + frete_aplicado
print("=" * 65)
print("🛒 RELATÓRIO DO CARRINHO FUNCIONAL (ESTADO IMUTÁVEL)")
print("=" * 65)
print(f"Identificador do Carrinho: {carrinho_original.id_carrinho}")
print(f"Total de SKUs Registrados: {len(carrinho_original.itens)}")
print(f"Subtotal Bruto (Soma Pura): R$ {subtotal_bruto:8.2f}")
print(f"Valor com Cupom (-15%): R$ {total_com_desconto:8.2f}")
print(f"Custo de Frete Calculado: R$ {frete_aplicado:8.2f}")
print(f"ICMS Incidente (8%): R$ {(valor_tributado - total_com_desconto):8.2f}")
print("-" * 65)
print(f"VALOR TOTAL LIQUIDADO: R$ {valor_final:8.2f}")
print("=" * 65)
print("\n[PROVA DE IMUTABILIDADE]:")
print(f"Carrinho original preservado intacto: {len(carrinho_original.itens)} itens.")
print(f"Itens promocionais filtrados separadamente: {len(itens_em_promocao)} itens.")
2. Tratamento Monádico de Erros sem Exceções (Result Monad)
No paradigma funcional estrito, funções não lançam exceções não tratadas, pois isso introduz saltos não determinísticos no fluxo de execução (goto implícito). O tratamento de falhas é realizado via tipos algébricos monádicos como Result[T, E]:
# monada_result_carrinho.py
from typing import Generic, TypeVar, Callable, Any
from dataclasses import dataclass
T = TypeVar("T")
E = TypeVar("E")
U = TypeVar("U")
# 1. Definição do Tipo Algébrico Result (Either: Right=Ok, Left=Err)
@dataclass(frozen=True)
class Ok(Generic[T]):
valor: T
def eh_sucesso(self) -> bool:
return True
def bind(self, fn: Callable[[T], "Result[U, E]"]) -> "Result[U, E]":
"""Aplica a função ao valor interno se for Ok."""
return fn(self.valor)
@dataclass(frozen=True)
class Err(Generic[E]):
erro: E
def eh_sucesso(self) -> bool:
return False
def bind(self, fn: Callable[[Any], Any]) -> "Err[E]":
"""Curto-circuita o pipeline mantendo o erro original."""
return self
Result = Ok[T] | Err[E]
# 2. Regras de Negócio Puras que retornam Result
def validar_carrinho_nao_vazio(itens: list[dict]) -> Result[list[dict], str]:
if not itens:
return Err("O carrinho de compras está completamente vazio.")
return Ok(itens)
def validar_limite_peso(itens: list[dict]) -> Result[list[dict], str]:
peso_total = sum(item["peso_kg"] * item["qtd"] for item in itens)
if peso_total > 30.0:
return Err(f"Peso total de {peso_total:.1f}kg excede o limite dos Correios (30kg).")
return Ok(itens)
def validar_cupom_promocional(cupom: str) -> Result[float, str]:
cupons_validos = {"TECH10": 0.10, "BLACKFRIDAY": 0.25, "VIP": 0.30}
if not cupom:
return Ok(0.0) # Sem cupom, desconto zero (sucesso)
if cupom in cupons_validos:
return Ok(cupons_validos[cupom])
return Err(f"Cupom '{cupom}' é inválido ou expirou.")
def calcular_total_liquidado(itens: list[dict], taxa_desconto: float) -> Result[float, str]:
subtotal = sum(item["preco"] * item["qtd"] for item in itens)
total_com_desconto = subtotal * (1.0 - taxa_desconto)
return Ok(total_com_desconto)
# 3. Execução com Encadeamento Monádico (Pipelines Railway-Oriented)
if __name__ == "__main__":
pedido_valido = [
{"nome": "Notebook Gamer", "preco": 5500.0, "qtd": 1, "peso_kg": 2.8},
{"nome": "Monitor 27 Pol", "preco": 1200.0, "qtd": 1, "peso_kg": 6.5},
]
pedido_pesado = [
{"nome": "Cadeira Ergonômica", "preco": 1800.0, "qtd": 2, "peso_kg": 18.0}, # Total = 36kg!
]
print("=== Pipeline 1: Pedido Válido com Cupom Válido ===")
res1 = (
validar_carrinho_nao_vazio(pedido_valido)
.bind(validar_limite_peso)
.bind(lambda itens: validar_cupom_promocional("TECH10")
.bind(lambda desc: calcular_total_liquidado(itens, desc))
)
)
match res1:
case Ok(valor):
print(f" ✅ Checkout Concluído com Sucesso! Total a pagar: R$ {valor:.2f}")
case Err(msg):
print(f" ❌ Falha no Checkout: {msg}")
print("\n=== Pipeline 2: Pedido com Cupom Inválido ===")
res2 = (
validar_carrinho_nao_vazio(pedido_valido)
.bind(validar_limite_peso)
.bind(lambda itens: validar_cupom_promocional("CUPOM_FALSO_2026")
.bind(lambda desc: calcular_total_liquidado(itens, desc))
)
)
match res2:
case Ok(valor):
print(f" ✅ Sucesso: R$ {valor:.2f}")
case Err(msg):
print(f" ❌ Falha interceptada pelo Monad: {msg}")
print("\n=== Pipeline 3: Pedido com Violação de Peso Máximo ===")
res3 = (
validar_carrinho_nao_vazio(pedido_pesado)
.bind(validar_limite_peso)
.bind(lambda itens: validar_cupom_promocional("VIP")
.bind(lambda desc: calcular_total_liquidado(itens, desc))
)
)
match res3:
case Ok(valor):
print(f" ✅ Sucesso: R$ {valor:.2f}")
case Err(msg):
print(f" ❌ Falha interceptada pelo Monad: {msg}")
Saída Esperada dos Scripts:
=================================================================
🛒 RELATÓRIO DO CARRINHO FUNCIONAL (ESTADO IMUTÁVEL)
=================================================================
Identificador do Carrinho: CART-BR-2026
Total de SKUs Registrados: 4
Subtotal Bruto (Soma Pura): R$ 1045.00
Valor com Cupom (-15%): R$ 888.25
Custo de Frete Calculado: R$ 0.00
ICMS Incidente (8%): R$ 71.06
-----------------------------------------------------------------
VALOR TOTAL LIQUIDADO: R$ 959.31
=================================================================
[PROVA DE IMUTABILIDADE]:
Carrinho original preservado intacto: 4 itens.
Itens promocionais filtrados separadamente: 2 itens.
=== Pipeline 1: Pedido Válido com Cupom Válido ===
✅ Checkout Concluído com Sucesso! Total a pagar: R$ 6030.00
=== Pipeline 2: Pedido com Cupom Inválido ===
❌ Falha interceptada pelo Monad: Cupom 'CUPOM_FALSO_2026' é inválido ou expirou.
=== Pipeline 3: Pedido com Violação de Peso Máximo ===
❌ Falha interceptada pelo Monad: Peso total de 36.0kg excede o limite dos Correios (30kg).