Pular para o conteúdo

A ponte para o Python

adopt Python.numpy — usar qualquer biblioteca Python de dentro do DataForge, sem cópia e sem mentira.

Uma linguagem que não alcança biblioteca nenhuma é uma ilha. Toda capacidade nova precisa ser reescrita do zero — foi assim que este projeto ganhou um Parquet próprio, um ChaCha20 próprio e 25 algoritmos de aprendizado próprios. Cada um correto, cada um a uma fração do que existe pronto lá fora.

O DataForge roda sobre Python e não alcançava nada dele. Esta é a porta.

adopt Python.<módulo>#

dataforge
adopt Python.numpy as np

a := np.array([1, 2, 3, 4])
out a * 2 + 1          # [3 5 7 9]
out a.sum(), a.mean()  # 10 2.5

É adopt normal — não há palavra reservada nova. Python é um espaço de nomes reservado, resolvido antes da biblioteca padrão e antes dos arquivos vizinhos.

dataforge
adopt Python.numpy as np              # o módulo inteiro
adopt Python.numpy.linalg as la       # um submódulo
adopt Python.json.{loads, dumps}      # só os nomes que interessam
adopt {sqrt} from Python.math         # a ordem invertida também vale

A ponte não converte#

Um ndarray continua um ndarray. É justamente por isso que a * 2 + 1 faz a conta vetorizada do numpy, em vez de virar um laço sobre um cluster de um milhão de posições.

Isso funciona porque o interpretador já trata objeto estranho pelo que ele faz, e não pelo que ele é — membro, método, índice, len, iteração, aritmética, texto e verdade já passavam por protocolo. A ponte não reimplementa nada disso.

Número é a exceção: typeof(a.sum()) responde Integer, não int64. Um escalar do numpy faz conta de inteiro, e given typeof(x) is "Integer" seria falso para um valor que soma, divide e compara como um. A regra vem do protocolo numbers do Python, não de numpy — Fraction e Decimal entram pela mesma porta.

Perguntar antes de depender#

Arcane.Ponte responde sem levantar erro, e é o que permite um programa se adaptar em vez de morrer:

dataforge
adopt Arcane.Ponte as Ponte

dados := [4.0, 8.0, 15.0, 16.0]
media := 0.0

given Ponte.tem("numpy"):
    adopt Python.numpy as np
    media := np.array(dados).mean()
otherwise:
    media := dados >> distill a, v: a + v 0 / len(dados)

out media

Explorar de dentro da linguagem#

Sem isto, descobrir o que um pacote oferece exige sair do DataForge e abrir a documentação dele. No REPL, é a diferença entre experimentar e desistir.

dataforge
adopt Python.json as json

out Ponte.atributos(json)
# [JSONDecodeError, JSONDecoder, JSONEncoder, codecs, decoder, …]

out Ponte.assinatura(json.dumps)
# dumps(obj, *, skipkeys=False, ensure_ascii=True, …)

out Ponte.doc(json.loads).lines()[0]
# Deserialize ``s`` (a ``str``, ``bytes`` or ``bytearray`` instance

Converter quando você quiser#

dataforge
forma := np.zeros([2, 3]).shape
out typeof(forma)                  # tuple
out typeof(Ponte.cluster(forma))   # Cluster
out Ponte.cluster(forma)           # [2, 3]

Serem explícitas é o ponto: a linha diz onde se paga a cópia.

A tabela inteira#

Faz
Ponte.tem(nome)o pacote está instalado? yes/no, sem levantar erro
Ponte.versao(nome)a versão instalada, ou void
Ponte.importar(nome)o módulo como valor — para quando o nome só se sabe rodando
Ponte.onde()o caminho do Python que está por trás
Ponte.empacotado()yes se for o executável único, que não tem pip
Ponte.atributos(x)os nomes públicos de um módulo ou objeto
Ponte.doc(x)a documentação que o Python carrega no objeto
Ponte.assinatura(x)como se chama, ou void se o Python não declara
Ponte.tipo(x)o nome do tipo do lado do Pythonndarray, DataFrame
Ponte.chamavel(x)dá para chamar?
Ponte.cluster(x)qualquer percorrível vira um Cluster
Ponte.vault(x)qualquer mapa vira um Vault

Quando o pacote não está lá#

A mensagem responde as três perguntas que você vai ter, nesta ordem: o que faltou, em qual Python faltou, e o comando exato para aquele Python.

text
erro[DF0501]: o pacote Python 'pandas' nao esta instalado.
   ┌─ analise.df:1:1
   │
 1 │ adopt Python.pandas as pd
   │ ^
   │
   = nota: o DataForge roda sobre ~/.dataforge/venv/bin/python3 —
           e e NESSE Python que o pacote precisa estar
   = dica: ~/.dataforge/venv/bin/python3 -m pip install pandas

O caminho exato importa: o instalador cria uma venv em ~/.dataforge. Quem roda pip install pandas no terminal instala no Python do sistema, que é outro, e o adopt continua falhando sem que nada explique por quê.

E o dataforge check avisa antes de rodar, porque ele pode provar que o pacote não está aqui:

text
analise.df:1:1: aviso: o pacote Python 'pandas' nao esta instalado aqui
    sugestão: se ele existir na maquina que vai RODAR o programa,
              este aviso nao se aplica

O que a ponte não protege#

adopt Python.os roda o código de inicialização do pacote, exatamente como um import faria. Não há sandbox aqui, e fingir que há seria pior que não ter.

Isso não acrescenta uma categoria de risco: a linguagem já tem Arcane.Process.run e escrita em disco. O que a ponte faz é tornar a fronteira visível na linha do `adopt` — quem lê o código sabe exatamente onde o programa deixou de ser portátil por conta própria.

A promessa de zero dependências continua inteira#

Nada em dataforge/ importa nada de fora. O que muda é que o programa de quem escreve passa a poder escolher as suas.

É a mesma distinção que separa "o Python não depende do numpy" de "o seu script pode depender". A palavra Python na linha do adopt é a declaração explícita de que a fronteira foi cruzada ali.