Skip to main content

acdg_foundations 0.1.0 copy "acdg_foundations: ^0.1.0" to clipboard
acdg_foundations: ^0.1.0 copied to clipboard

Recriação fiel, em Dart puro e sem dependências, dos protocolos e tipos de valor fundamentais da stdlib do Swift: Optional, Equatable, Hashable, Comparable, Identifiable, Copyable e Escapable.

acdg_foundations #

Recriação fiel — em Dart puro, sem nenhuma dependência — dos protocolos e tipos de valor fundamentais da stdlib do Swift. A meta é preservar a intenção e a consequência prática de cada construção, não só o nome.

Swift Aqui O que você implementa O que ganha de graça
Optional<Wrapped> Optional (Some/None) — map, flatMap, or/orElse (??), unwrapped (! com trap), match, ifSome, toNullable
Equatable mixin Equatable isEqual operator == (relação de equivalência)
Hashable: Equatable mixin Hashable on Equatable hash(Hasher) hashValue, hashCode, chave de Set/Map
Comparable: Equatable mixin Comparable<T> on Equatable isLessThan <, <=, >, >=, compareTo (List.sort)
Identifiable mixin Identifiable<ID> id identidade estável; + ObjectIdentifier, ReferenceIdentifiable
Copyable interface Copyable<Self> copy() contrato de cópia independente
~Copyable (move-only) base NonCopyable (opcional) onConsume borrow/consume/move com trap em use-after-consume
BitwiseCopyable mixin BitwiseCopyable — marcador de tipo trivial (memcpy)
Escapable mixin Escapable — marcador
~Escapable (lifetime) base NonEscapable + borrowing(...) guard ensureInScope() trap se o valor "escapar" do escopo
CustomStringConvertible mixin CustomStringConvertible description toString() delega para description
CustomDebugStringConvertible mixin CustomDebugStringConvertible debugDescription usado por reflecting(...)
LosslessStringConvertible mixin … on CustomStringConvertible + LosslessStringFactory description + call(String) round-trip T(x.description) == x
CaseIterable mixin CaseIterable<Self> allCases em enums, equivale ao values nativo
RawRepresentable mixin RawRepresentable<RawValue> + RawRepresentableFactory rawValue + call(raw) round-trip T(rawValue: x.rawValue) == x

Os "init falíveis" do Swift (init?(_:), init?(rawValue:)) viram objetos-fábrica invocáveis (LosslessStringFactory, RawRepresentableFactory), já que o Dart não permite exigir construtores em interfaces. String(describing:) / String(reflecting:) viram as funções describing(...) / reflecting(...) com a mesma ordem de prioridade.

Decisões de fidelidade #

  • Equatable ≠ Hashable: como no Swift, Equatable fornece só ==. Quem dá hash (e habilita Set/Map) é Hashable.
  • Comparable: só < é exigido; os demais operadores derivam exatamente como no Swift (a <= b ≡ !(b < a)). O nome Comparable sombreia o do dart:core de propósito — e conformamos a ele, então List.sort funciona.
  • Optional: o force-unwrap (unwrapped) lança OptionalUnwrapError, espelhando o trap de nil!.
  • ~Copyable / ~Escapable: o Dart não tem move semantics nem lifetimes, então emulamos a consequência em tempo de execução (o compilador Swift faz em tempo de compilação): usar um valor consumido/escapado lança erro.

Initialization with Literals (ExpressibleBy*Literal) #

Os protocolos que, no Swift, deixam o compilador converter um literal diretamente num tipo custom (let c: Celsius = 37.5). O Dart não tem conversão de literal definível pelo usuário, então — como nos outros init — eles são reificados como fábricas, preservando a consequência (construir o tipo a partir do literal):

final class CelsiusLiteral
    implements ExpressibleByIntegerLiteral<Celsius>, ExpressibleByFloatLiteral<Celsius> {
  const CelsiusLiteral();
  @override Celsius integerLiteral(int v) => Celsius(v.toDouble());
  @override Celsius floatLiteral(double v) => Celsius(v);
}

const CelsiusLiteral().floatLiteral(37.5); // Celsius(37.5)

Inclui a hierarquia de string (…UnicodeScalarLiteral ← …ExtendedGraphemeClusterLiteral ← ExpressibleByStringLiteral ← ExpressibleByStringInterpolation), o motor de interpolação (StringInterpolationProtocol / DefaultStringInterpolation) que você dirige na mão como o compilador faz com "Olá \(nome)!", e StaticBigInt (literais inteiros de precisão arbitrária).

Encoding / Decoding (Codable) #

A arquitetura inteira do Codable do Swift: Encodable/Decodable/Codable, Encoder/Decoder, os containers (keyed/unkeyed/single-value, com o split …Protocol + box), CodingKey & cia., e EncodingError/DecodingError com codingPath. Vem com coders concretos JSONEncoder/JSONDecoder e um JSONSerialization (writer + parser) escrito do zero, sem dart:convert.

enum UserKey implements CodingKey {
  id('id'), fullName('full_name'); // mapeia para chaves customizadas
  const UserKey(this.stringValue);
  @override final String stringValue;
  @override int? get intValue => null;
}

final class User implements Codable {
  const User(this.id, this.fullName);
  final int id;
  final String fullName;

  @override
  void encode(Encoder encoder) {
    final c = encoder.container<UserKey>();
    c.encode(id, forKey: UserKey.id);
    c.encode(fullName, forKey: UserKey.fullName);
  }

  // `init(from:)` reificado como fábrica (Dart não exige construtores em interface):
  static const Decodable<User> decoder = _UserDecoder();
}

final json = const JSONEncoder().encodeToString(const User(7, 'Gabriel'));
// {"id":7,"full_name":"Gabriel"}
final user = const JSONDecoder().decodeFromString(User.decoder, json);

Adaptações fiéis às limitações do Dart: init(from:)/init?(...) viram fábricas reificadas (Decodable<T>); sem overload por tipo, encode recebe Object? e o decode usa primitivos tipados (decodeInt/decodeString…) + o genérico decode<T>(Decodable<T>, forKey:) (a fábrica no lugar do T.self).

Optional Binding (if let / guard let / switch) #

As estruturas de controle que vinculam condicionalmente o valor desempacotado de um Optional:

// if let value = opt { ... } else { ... }
opt.ifLet((v) => use(v), orElse: () => semValor());

// if let value = opt, where(value) { ... }
opt.ifLet((v) => use(v), where: (v) => v > 5);

// guard let value = opt else { <SAI do escopo> }
final v = opt.guardLet(orElse: () => throw ArgumentError('nil'));
// `v` é não-opcional daqui pra baixo.

// guard let a = optA, let b = optB else { return fallback }
final r = binding<int>(
  (let) => let(optA) + let(optB), // qualquer None → orElse
  orElse: () => 0,
);

// switch opt { case .some(let v): ...; case .none: ... }  — nativo do Dart
final texto = switch (opt) {
  Some(:final value) => 'some($value)',
  None() => 'none',
};

O detalhe mais fiel é o guard let: o orElse tem tipo Never Function(), então o compilador exige que esse ramo não retorne (precisa throw/sair) — exatamente como o else de um guard no Swift é obrigado a deixar o escopo. Tentar orElse: () => 0 é erro de compilação (return_of_invalid_type_from_closure).

Performance e quando usar (vs Dart canônico) #

Comparação honesta, com benchmarks medidos (Apple M2, AOT). O Dart canônico é mais rápido em quase tudo; a vantagem desta lib é ergonomia/explicitude.

Cenário Canônico acdg_foundations Razão
Igualdade + Set ~103 µs ~130 µs 1,3× mais lento
Optional vs nullable ~0,9 µs ~37 µs ~41× mais lento (boxing)
JSON encode ~42 µs ~81 µs 1,9× mais lento
JSON decode ~22 µs ~60 µs 2,6× mais lento
Sort Comparable ~313 µs ~214 µs 1,5× mais rápido

Há ainda benchmarks de alocação/GC, payloads grandes e paralelismo com isolates (com testes de corretude em test/concurrency_test.dart provando que os value types são "Sendable" entre isolates). Resumo: o Optional troca memória

  • GC por expressividade (evite em coleções grandes/loops quentes); isolates dão speedup real porém sublinear (limitado pela cópia de mensagem).

Regra de ouro: acdg_foundations para expressividade na borda do domínio; Dart canônico (T?, dart:convert) para performance no núcleo e hot paths. Detalhes, prós/contras por recurso e o guia completo em doc/comparativo-dart-canonico.md. Rode os benchmarks:

dart compile exe benchmark/comparison_benchmark.dart   -o /tmp/bench  && /tmp/bench
dart compile exe benchmark/allocation_benchmark.dart   -o /tmp/alloc  && /tmp/alloc
dart compile exe benchmark/concurrency_benchmark.dart  -o /tmp/conc   && /tmp/conc

🧭 Isolates valem a pena num app comum? Quase nunca. Payloads pequenos decodificam em <2 ms e o spawn + cópia de mensagem custa mais que o trabalho. Vá para isolate (via compute() no Flutter) só quando: (1) Flutter com payload grande travando a UI — o ganho aqui é responsividade, não throughput (até K=1 ajuda) — ou (2) worker/CLI de lote (import/export/ ETL) e compute pesado (imagem, cripto). E nesse hot path use dart:convert, não esta lib. Detalhes na nota completa do doc.

Como usar em outro projeto #

Este package não está publicado no pub.dev. Importe por caminho ou git no pubspec.yaml do projeto consumidor:

dependencies:
  acdg_foundations:
    path: ../acdg-foundations        # caminho local
    # ou:
    # git:
    #   url: https://github.com/acdgbrasil/acdg-foundations.git
import 'package:acdg_foundations/acdg_foundations.dart';

final class Money with Equatable, Hashable, Comparable<Money> {
  const Money(this.cents);
  final int cents;

  @override
  bool isEqual(Money other) => cents == other.cents;
  @override
  void hash(Hasher hasher) => hasher.combine(cents);
  @override
  bool isLessThan(Money other) => cents < other.cents;
}

void main() {
  final precos = <Money>{const Money(990), const Money(990)}; // Hashable → dedup
  print(precos.length); // 1

  final ordenado = [const Money(3), const Money(1), const Money(2)]..sort();
  print(ordenado.map((m) => m.cents)); // (1, 2, 3)

  print(const Optional.some(21).map((v) => v * 2).or(0)); // 42
}

Veja example/acdg_foundations_example.dart para os sete em ação.

Desenvolvimento #

dart pub get
dart analyze
dart test
dart run example/acdg_foundations_example.dart

Licença #

BSD-3-Clause © 2026 ACDG Technology.

0
likes
150
points
75
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Recriação fiel, em Dart puro e sem dependências, dos protocolos e tipos de valor fundamentais da stdlib do Swift: Optional, Equatable, Hashable, Comparable, Identifiable, Copyable e Escapable.

Repository (GitHub)
View/report issues

License

BSD-3-Clause (license)

More

Packages that depend on acdg_foundations