Skip to content

Nim for Python Programmers ES

Juan Carlos edited this page Feb 2, 2021 · 31 revisions

Tabla De Contenidos

Comparacion Objetos Es necesario saber C? Tuplas Nombradas
Variables self.__init__() Strings Listas
Variable Naming Consistent Spacing Scoping Mutable arguments
Imports Ranges Operaciones de String Comprension de Lista
try: import Static Bounds Checks Null Coalescing with Context Manager
Arrays Slices Tuplas Comprension de Diccionario
Comprension de Set Leer y Escribir archivos Decoradores Lambdas
Sets JSON Map & Filter Indentacion Opcional
Diccionarios CamelCase DocStrings Importar archivos Nim en Python
Operador Ternario Unittests def Vs proc / func Auto-Ejecucion de Modulo Principal
Syntax Python para Nim Publicar a PYPI Compilacion Silenciosa Ayuda de Compilador
Modos de Compilacion Abstract Base Classes Decoradores WebAssembly
Templates Nim corriendo Interpretado Nim en el browser Equivalencias de Libreria Standard
Instalar Paquetes Nim desde PIP Instalar Nim desde PIP Cheatsheet PDF Arrow Functions
Pattern Matching Tutorial 1 en Espanol Tutorial 2 en Espanol Aprende Nim en 5 Minutos
Arduino, MicroPython, CircuitPython LiveCoding, FoxDot, SuperCollider Como compartir variables entre funciones? Async

Comparison

Caracteristica 🐍 Python 👑 Nim
Modelo de Ejecucion Maquina Virtual (Interprete) Codigo Maquina via C/C++ (Compilador)
Escrito usando C Nim
Licencia Python Software Foundation License MIT
Version (Mayor) 3.x 1.x
Meta-programacion ✔️ metaclass, eval ✔️ template, macro
Manejo de Memoria Garbage collector Estrategias de manejo de memoria multi-paradigma (garbage collectors o ARC o manual)
Tipado Dinamico Estatico
Tipos Dependientes ✔️
Genericos Duck typing ✔️
Tipos int8/16/32/64 ✔️
Tipos float32/float64 ✔️
Tipo Char ✔️
Tipo Subrange ✔️
Tipo Enum ✔️ ✔️
Tipo Bigint ✔️ ✔️
Tipo Array ✔️ ✔️
Tipado con Inferencia Duck typing ✔️
Closures ✔️ ✔️
Sobrecarga de Operadores ✔️ ✔️ en cualquier Tipo
Operadores Personalizados ✔️
Orientado a Objetos ✔️ ✔️
Metodos ✔️ ✔️
Excepciones ✔️ ✔️
Funciones Anonimas ✔️ 1-linea solamente ✔️ multi-linea
Comprension de Lista ✔️ ✔️
Comprension de Diccionario ✔️ ✔️
Comprension de Set ✔️ ✔️
Comprension de Objetos Personalizados ✔️
Inmutabilidad Limitado (frozenset, tupla, etc) ✔️
Inmutabilidad de Argumentos de Funcion Mutable Inmutable
String Literales Formateados ✔️ F-Strings ✔️ strformat
FFI ✔️ CTypes (solo C) ✔️ C/C++/JS
Async ✔️ ✔️
Threads ✔️ (Global Interpreter Lock) ✔️
Regex ✔️ No Perl-compatible ✔️ Perl Compatible Regular Expressions
Comentarios de Auto-Documentacion ✔️ Strings Texto-plano ✔️ ReStructuredText/Markdown
Publicacion de Paquetes ✔️ No integrado, requiere twine ✔️ Integrado, nimble
Manejador de Paquetes ✔️ pip ✔️ nimble
AutoFormateador de Codigo ✔️ black via PIP ✔️ nimpretty Integrado
Extensiones de Archivo .py, .pyi, .pyd, .pyo, .pyw, .pyz, .pyx .nim, .nims
Formato Temporario de Representacion Intermedia .pyc C
Usa SheBang en Archivos ✔️
Indentacion Tabuladores y Espacios Espacios

Variables

Crear una nueva usa var o let o const. Nim tiene inmutabilidad y ejecucion de funciones en tiempo de compilacion. Podes asignar funciones a variables.

Declaracion Tiempo de Compilacion Tiempo de Ejecucion Inmutable Requiere Asignacion Auto-Inicializado
var ✔️ ✔️
let ✔️ ✔️ ✔️ ✔️
const ✔️ ✔️ ✔️ ✔️

Para usuarios avanzados, es posible saltarse la Auto-Initializacion de variables.

Las variables pueden ser multi-linea sin "escaparlas", es util para lineas largas y operadores ternarios largos, ejemplo minimo:

variable = 666 +  \
  420 *  \
  42 -   \
  9           

assert variable == 18297

⬆️ Python ⬆️          ⬇️ Nim ⬇️

var variable = 666 +
  420 *
  42 -
  9

assert variable == 18297

Funciona para las funciones tambien:

import strutils

var variable = "  12345  "
  .strip
  .parseInt

assert variable == 12345

Podes usar guion bajo (underscore), espacios y salto de linea en los nombres de variables:

let `this must  be
     positive`: Positive = 42

assert this_must_be_positive == 42

const `this is my nice named variable` = 42

Podes usar nombres reservados como nombre de variable.

Si estas recien comenzando desde cero, podes usar var para todo cuando estas aprendiendo, no produce error por hacerlo.

Consistent Spacing

Los espacios deben ser consistentes en el codigo, principalmente en operadores:

echo 2 - 1 # OK
echo 2-1   # OK

Espacios inconsistentes:

echo 2 -1 # Error
#      ^ es leido como "-1"

Omitir espacios en el codigo no mejora en nada. Todos los operadores son funciones en Nim.

Scoping

for x in range(0, 9):
  if x == 6:
    print(x)

print(x)

Resultados:

6
8  # Leak!

⬆️ Python ⬆️          ⬇️ Nim ⬇️

for x in 0..9:
  if x == 6:
    echo x

echo x

Resultados:

Error: undeclared identifier: 'x'

Mas ejemplos:

x = 0
y = 0

def example():
  x = 1
  y = 1
  class C:
    assert x == 0 and y == 1  # ???
    x = 2

example()

⬆️ Python ⬆️          ⬇️ Nim ⬇️

var x = 0
var y = 0

proc example() =
  var x = 1
  var y = 1
  type C = object
  assert x == 1 and y == 1
  x = 2

example()

Mas del mismo ejemplo:

x = 0
y = 0

def example():
  x = 1
  y = 1
  class C:
    assert x == 0 and y == 0  # ???
    x = 2
    try:
      raise
    except Exception as y:
      pass

example()

⬆️ Python ⬆️          ⬇️ Nim ⬇️

var x = 0
var y = 0

proc example() =
  var x = 1
  var y = 1
  type C = object
  assert x == 1 and y == 1
  x = 2
  try:
    raise
  except Exception as y:
    discard

example()

Mutable arguments

def example(argument = [0]):
  argument.append(42)
  return argument

print(example())
print(example())
print(example())

Output:

[0, 42]
[0, 42, 42]
[0, 42, 42, 42]

⬆️ Python ⬆️          ⬇️ Nim ⬇️

func example(argument = @[0]): auto =
  argument.add 42
  return argument

echo example()
echo example()
echo example()

Output:

Error: type mismatch: got <seq[int], int literal(42)>

but expected one of: 
proc add[T](x: var seq[T]; y: sink T)
  first type mismatch at position: 1
  required type for x: var seq[T]
  but expression 'argument' is immutable, not 'var'

Imports

Import 🐍 Python 👑 Nim
Solo 1 simbolo, usar sin calificar from math import sin from math import sin
Todos los simbolos, usar sin calificar from math import * import math (recomendado)
Todos los simbolo, usar calificado import math (recomendado) from math import nil
"import as" con otro nombre import math as papota import math as papota
Todos los simbolos excepto 1, usar sin calificar import math except sin
Todos los simbolos excepto 3, usar sin calificar import math except sin, tan, PI
Incluir otro modulo en este modulo include algunmodulo

En Nim, import math importa todos los simbolos del modulo math (sin(), cos(), etc) entonces se pueden usar sin calificar. El equivalente en Python es from math import *.

Si preferis no importar todos los simbolos, y usar los nombres calificados, en Nim es from math import nil. Entonces puedes llamar math.sin() etc. El equivalente en Python es import math.

Las razon por la cual es seguro importar todos los nombres en Nim es que el compilador realmente no incluira ninguna funcion o nombre no usado (entonces no hay un costo extra), y por que Nim es estaticamente tipado entonces puede distinguir entre dos funciones importadas con el mismo nombre basandose en los Tipos y argumentos de la funcion. Y aun asi en casos muy raros donde los nombres y tipos y argumentos son todos iguales, puedes usar el nombre completo calificado para desambiguar.

Nim puede usar los imports en una misma linea. De Python a Nim ejemplo lo mas minimo posible:

import foo
import bar
import baz

⬆️ Python ⬆️          ⬇️ Nim ⬇️

import foo, bar, baz

Si tu linea queda demasiado larga, los import tambien pueden estar en un bloque indentado:

import
  foo, bar, baz, more, imports,
  here, we, split, multiple, lines

Si preferis 1 import por linea, por los Diff de Git, los import tambien pueden estar 1 por linea:

import
  foo,
  bar,
  baz

La forma con 1 import por linea es comun en in Python y Nim, pero en Nim la forma import foo, bar, baz es mas frecuente.

Mas ejemplos:

## Esto es documentacion del modulo.
#  Esto es un comentario.
include prelude
import sugar as stevia
from math import nil
from with as what import nil

Si estas recien comenzando desde cero, podes usar los import como en Python para todo cuando estas aprendiendo, no produce error por hacerlo.

Programatically

__import__("math")

⬆️ Python ⬆️          ⬇️ Nim ⬇️

template imports(s) = import s
imports math

Code without imports

A veces veras ejemplos de codigo o archivos sin los imports pero funcionan igualmente (?).

Nim puede usar import desde la linea de comando de compilacion, o dese un archivo .nims:

  • nim c --import:sugar file.nim
  • nim c --import:folder/mymodule file.nim
  • nim js --import:strutils --include:mymodule file.nim

Algunas veces los projectos o ejemplos de codigo usan esto para salvar tipear tanto.

Ver:

Prelude

Puede parecer que Python tiene mas simbolos disponibles por defecto sin ningun import comparado con Nim, para tener una experiencia similar puedes usar prelude:

include prelude

echo now()
echo getCurrentDir()
echo "Hello $1".format("World")

prelude funciona para JavaScript tambien.

Where Symbols come from?

  • Si los simbolos son no calificados, como saber de donde vienen?

Si foo() es un simbolo:

  • Python: Tipicamente tenes object.foo() en lugar de module.foo(), sin UFCS.
  • Nim: Tipicamente tenes foo(), con UFCS.

Typicamente el Editor/IDE debe mostrar de donde viene un simbolo, como cualquier otro lenguaje de programacion:

Nim viene con NimSuggest para Editor/IDE.

Contrario a Python, el sistema de tipos de Nim tiene toda la info de los simbolos:

import macros
macro findSym(thing: typed) = echo thing.getType.lineInfo

findSym:
  echo  # De donde sale echo?.

echo viene de:

lib/system.nim(1929, 12)

Exports

En Python todos los simbolos en un modulo son visibles desde otro modulo que lo importa, incluido simbolos "dunder" y cosas que no deberian ser mutadas desde afuera.

En Nim todo es privado por defecto y no es visible desde otro modulo que lo importa, para hacer un simbolo Publico y visible desde afuera podes usar la Star *:

let variable* = 42
const constante* = 0.0
proc unaFuncion*() = discard
template unTemplate*() = discard
type Platipo* = object
  fufflyness*: int

Star hace el simbolo visible desde el mundo exterior, hace el simbolo aparecer en la documentacion generada, cuando importas el modulo el simbolo estara en el namespace, pero los simbolos internos de detalles de implementacion sin * no son visibles, y las cosas que no deberian ser mutadas desde afuera no seran visibles, tiene el beneficio visual para humanos que es facil reconocer "la API Publica" de un modulo con solo ver el codigo.

Para entender mejor, es recomendado leer (en Ingles): https://narimiran.github.io/2019/07/01/nim-import.html

try: import

A veces en Python ves este tipo de construcciones:

try:
  import modulo
except:  # ImportError
  pass   # Explota en run-time (?)

try:
  import modulo
except:  # ImportError
  def funcion(): # Ultimo recurso definicion de "emergencia" (duplicacion de codigo)
    return algunvalor
  # Mas codigo aca...

Nim resuelve todos los import en tiempo de compilacion, no hay ImportError en tiempo de ejecucion.

No hay necesidad de try: import en Nim.

Arrays

Los Arrays son de capacidad fija, comienzan en indice 0 y contienen elementos del mismo Tipo.

Cuando pasas un array a una funcion en Nim, el argumento es una referencia inmutable. Nim va agregar chequeos de limite de capacidad en tiempo de ejecucion en los array.

Podes usar openarray para aceptar un array de cualquier capacidad en argumentos de funcion, y podes usar low(el_array) y high(el_array) para averiguar los limites de capacidad del array.

string es compatible con openArray[char] para omitir copias innecesarias por optimizacion, char es compatible con int, entonces la manipulacion de string puede hacerse usando matematica transparentemente, una funcion que toma openArray[char] acepta "abcd" y ['a', 'b', 'c', 'd'].

Ver:

Objects

Los Objetos en Nim son un poco diferentes de classes en Python. Los Objetos soportan herencia y Orientacion a Objetos. Classes son Tipos nombrados en Nim. Las Funciones flotan sueltas libremente, no estan metidas dentro de los objetos (igualmente, podes usarlas como en Python), podes llamar a una funcion de objeto con Objeto.funcion(). Nim no tiene un self o this implicito.

Imagina que las funciones son "pegadas" a los objetos durante la compilacion, entonces podes usarlas en tiempo de ejecucion como si fueran classes y metodos de Python.

Objetos Python que internamente usan generacion de codigo son muy muy lentos, dataclass, metaclass, Decoradores, etc pueden ser mas de 30x mas lentos que un class normal, y derrota cualquier optimizacion, incluyendo un archivo .pyc, Expansion de codigo en Nim es realizada en tiempo de compilacion, haciendolo costo cero en tiempo de ejecucion.

De Python a Nim ejemplo lo mas minimo posible:

class Gatito(object):
    """ Documentacion Aca """

    def purr(self):
        print("Miau Miau")

Gatito().purr()

⬆️ Python ⬆️          ⬇️ Nim ⬇️

type Gatito = object  ## Documentacion Aca
proc purr(self: Gatito) = echo "Miau Miau"
Gatito().purr()

Ejemplo de Orientacion a Objetos estilo Python:

type Animal = ref object of RootObj ## Animal objeto base.
  edad: int
  nombre: string                    ## Atributos de objeto base.

type Gato = ref object of Animal    ## Gato heredado.
  jugueton: float                   ## Atributos de objeto heredado.

func incrementar_edad(self: Gato) =
  self.edad.inc()                   # Gato funcion de objeto, accede y *modifica* el objeto.

var gatito = Gato(nombre: "Tom")    # Gato instancia de objeto.
gatito.incrementar_edad()           # Gato funcion de objeto usado.

assert gatito.nombre == "Tom"       # Assert en objeto Gato.
assert gatito.edad == 1

self.__init__()

Luego del ejemplo de Gato probablemente estas pensando como hacer un def __init__(self, arg):.

El __init__() de Python es el newObject() o initObject() de Nim, podemos hacer un __init__() para el Gato:

type Gato = object                # Gato objeto.
  edad: int
  nombre: string                  # Atributos de Gato.

func initGato(edad = 2): Gato =  # Gato.__init__(self, edad=2)
  result.edad = edad             # self.edad = edad
  result.nombre = "adoptame"     # self.nombre = "adoptame"

var gatito = initGato()          # Gato instancia de objeto.

assert gatito.nombre == "adoptame" # Assert en el Gato.
assert gatito.edad == 2

Los nombres son una convencion y mejores practicas, cuando quieras un init para Foo simplemente hace newFoo() o initFoo(). Como habras notado initGato es solo una funcion que retorna un Gato.

  • initFoo() para object.
  • newFoo() para ref object.

Lee la documentation para Nombrar cosas siguiendo las mejores practicas.

Object Attribute Default Values

El constructor de Objeto es tambien la forma de poner valores personalizados por defecto a los atributos de tus objetos:

type Cat = object
  age: int                 # AutoInicializado a 0
  name: string             # AutoInicializado a ""
  playfulness: float       # AutoInicializado a 0.0
  sleeping: bool           # AutoInicializado a false 
func initCat(): Cat =    
  result.age = 1           # Poner el valor a 1
  result.name = "Bastet"   # Poner el valor a "Bastet"
  result.playfulness = 9.0 # Poner el valor a 9.0
  result.sleeping = true   # Poner el valor a true

Ranges

En Python, los bucles de enteros usan el range. Para los usos de 1 y 2 argumentos de esta funcion, El .. iterador de Nim funciona igual:

for i in 0..10:
  echo i  # Muestra 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

for i in 5..10:
  echo i  # Muestra 5, 6, 7, 8, 9, 10

Nota que .. incluye el final del rango (es inclusivo), donde en Python range(a, b) no incluye b (no inclusivo). Si preferis la forma Python, usa el ..< iterador:

for i in 0..<10:
  echo i  # Muestra 0, 1, 2, 3, 4, 5, 6, 7, 8, 9

El Python range() tambien tiene un tercer parametro opcional, que es el valor de incrementacion de cada paso, que puede ser positivo o negativo. Si necesitas este comportamiento, usa countup o countdown:

for i in countup(1, 10, 2):
  echo i  # Muestra 1, 3, 5, 7, 9

Ver:

Slices

La sintaxis de slice es un poco diferente. El a[x:y] de Python es a[x..<y] de Nim.

let variable = [1, 2, 3, 4]
assert variable[0..0] == @[1]
assert variable[0..1] == @[1, 2]
assert variable[0..<2] == @[1, 2]
assert variable[0..3] == @[1, 2, 3, 4]

Reverse Index Slices

En Nim los indices reversos usan ^ con el numero, por ejemplo ^1, los indices reversos tiene un Tipo especifico BackwardsIndex, y tambien pueden ser "preparados" en tiempo de compilacion como un const:

const ultimo = ^1  # Compile-time
assert ultimo is BackwardsIndex
assert [1, 2, 3, 4, 5][2 .. ultimo] == @[3, 4, 5]
assert [1, 2, 3, 4, 5][2 .. ^1] == @[3, 4, 5]
var otro = ^3    # Run-time
assert [1, 2, 3, 4, 5][0 .. otro] == @[1, 2, 3]
assert [1, 2, 3, 4, 5][^3 .. ^1] == @[3, 4, 5]

Static Bounds Check

Comparemos unos ejemplos simplificados:

[0, 1, 2][9]  # No existe el Indice 9

Explota en tiempo de ejecucion por que no existe el indice 9:

$ python3 ejemplo.py
Traceback (most recent call last):
  File "ejemplo.py", line 1, in <module>
    [0, 1, 2][9]
IndexError: list index out of range

$

Veamos que hace Nim:

discard [0, 1, 2][9]  # No existe el Indice 9

Compilar y ejecutar:

$ nim compile --run ejemplo.nim
ejemplo.nim(1, 19) Warning: can prove: 9 > 2  [IndexCheck]
ejemplo.nim(1, 18) Error: index 9 not in 0..2 [0, 1, 2][9]

$

Nim chequea en tiempo de compilacion que [0, 1, 2] no tiene indice 9, por que 9 > 2, no compila ni ejecuta.

Esto tambien funciona con Subrange, digamos que tenes una variable con un numero entero que debe ser positivo:

let debe_ser_positivo: Positive = -9

Compilar y ejecutar:

$ nim compile --run ejemplo.nim
ejemplo.nim(1, 34) Warning: can prove: 1 > -9 [IndexCheck]
ejemplo.nim(1, 34) Error: conversion from int literal -9 to Positive is invalid.

$

Nim chequea en tiempo de compilacion que debe_ser_positivo no es Positive por que 1 > -9, no compila ni ejecuta.

Podes controlar esto con --staticBoundChecks:on o --staticBoundChecks:off.

Con --staticBoundChecks:off puede lanzar error en tiempo de ejecucion como en Python.

Null Coalescing

Python no tiene un Null Coalescing Operator (al tiempo de escribir).

Python usa este tipo de construcciones:

otro = bar if bar is not None else "valor por defecto"  # "bar" puede ser Null?, o no ?.

Nim tiene el modulo wrapnils con el Null Coalescing Operator ?., que simplifica el codigo reduciendo la necesidad de ramas de if..elif...else en valores intermedios que pueden ser Null.

assert ?.foo.bar.baz == ""  # "bar" puede ser Null?, o no ?.

Null es None en Python. Null es nil en Nim.

Ver https://nim-lang.github.io/Nim/wrapnils.html

With Context Manager

Para un "With Context Manager" (Manejador de Contextos) en Nim tenes las siguientes opciones:

Strings

Lenguaje String Multi-line string Raw String Multi-line Raw string Formatted Literals Quote
🐍 Python "foo" """foo""" r"foo" r"""foo""" f"""{1 + 2}""" " '
👑 Nim "foo" """foo""" r"foo" r"""foo""" fmt"""{1 + 2}""" "

String Ops

Ops 🐍 Python 👑 Nim
Minuscula "ABCD".lower() "ABCD".toLowerAscii()
Desnudar " ab ".strip() " ab ".strip()
Dividir "a,b,c".split(",") "a,b,c".split(",")
Concatenacion "a" + "b" "a" & "b"
Buscar "abcd".find("c") "abcd".find("c")
Comienza Con "abc".startswith("ab") "abc".startswith("ab")
Termina Con "abc".endswith("ab") "abc".endswith("ab")
Dividir Lineas "1\n2\n3".splitlines() "1\n2\n3".splitlines()
Partir "abcd"[0:2] "abcd"[0..<2]
Partir 1 char "abcd"[2] "abcd"[2]
Partir Revertido "abcd"[-1] "abcd"[^1]
Normalizar unicodedata.normalize("NFC", "Foo") "Foo".normalize()
Contar Lineas len("1\n2\n3".splitlines()) "1\n2\n3".countLines()
Repetir "foo" * 9 "foo".repeat(9)
Indentar textwrap.indent("foo", " " * 9) "a".indent(9)
Desindentar textwrap.dedent("foo") "foo".unindent(9)
Parsear Booleano bool(distutils.util.strtobool("fALse")) parseBool("fALse")
Parsear Entero int("42") parseInt("42")
Parsear Flotante float("3.14") parseFloat("3.14")
String Literal Formateado f"foo {1 + 2} bar {variable}" fmt"foo {1 + 2} bar {variable}"
Distancia de Levenshtein editDistance("Kitten", "Bitten")

String Efficiency

Strings con 1 sola alocacion de memoria son posibles con newStringOfCap(capacity = 42), que retorna 1 nuevo string vacio "" pero con capacidad alocada de 42, pero si pasas mas alla de esa capacity no va a dar error (buffer overflow):

variable = ""
assert variable == "" # longitud 0, capacidad 0, 1 alloc, 0 copias
variable += "a"       # longitud 1, capacidad is 1, 2 alloc, 1 copias
variable += "b"       # longitud 2, capacidad is 2, 3 alloc, 2 copias
variable += "c"       # longitud 3, capacidad is 3, 4 alloc, 3 copias
variable += "d"       # longitud 4, capacidad is 4, 5 alloc, 4 copias
assert variable == "abcd" 
# TOTAL: 5 alloc, 4 copias

⬆️ Python ⬆️          ⬇️ Nim ⬇️

var variable = newStringOfCap(2)
assert variable == "" # longitud 0, capacidad is 2, 1 alloc, 0 copias
variable.add "a"      # longitud 1, capacidad is 2, 1 alloc, 0 copias
variable.add "b"      # longitud 2, capacidad is 2, 1 alloc, 0 copias
variable.add "c"      # longitud 3, capacidad is 3, 2 alloc, 0 copias
variable.add "d"      # longitud 4, capacidad is 4, 3 alloc, 0 copias
assert variable == "abcd" 
# TOTAL: 3 alloc, 0 copias

Esta diferencia es mas grande para strings adento de bucles for o while.

F-Strings

Nim strformat implementa string literales formateados inspirados de Python F-string. strformat es implementado usando metaprogramming y la expansion de codigo es en tiempo de compilacion. Funciona para JavaScript tambien.

Similar a Python F-string se puede debugear usando el simbolo igual, fmt"{key=}" expande a fmt"key={value}":

let x = "hello"
assert fmt"{x=}" == "x=hello"
assert fmt"{x   =  }" == "x   =  hello"

Nim strformat soporta Backslash, pero Python F-string no:

>>> print( f"""{ "yep\nope" }""" ) # Run-time error.
Error: f-string expression part cannot include a backslash.

⬆️ Python ⬆️          ⬇️ Nim ⬇️

echo fmt"""{ "yep\nope" }"""       # Nim works.

yep
ope

Se puede elejir los caracteres delimitadores:

import strformat
let variable = 42
assert fmt("( variable ) { variable }", '(', ')') == "42 { variable }"
assert fmt("< variable > { variable }", '<', '>') == "42 { variable }"

Caracteres Backtick y Space ' ' tambien funciona:

import strformat
let variable = 42
assert fmt(" variable`{variable}", ' ', '`') == "42{variable}"

Ver:

Standard Library Equivalents

Uso 🐍 Python 👑 Nim
Sistema Operativo os os
Operaciones String string strutils
Fecha y Hora datetime times
Cosas Al Azar random random
Expresiones Regulares re re
HTTP urllib httpclient
Logs logging logging
Ejecutar comandos subprocess osproc
Manipulacion de ruta pathlib, os.path os
Matematica math, cmath math
Tipos MIME mimetypes mimetypes
SQLite SQL sqlite3 db_sqlite
Postgres SQL db_postgres
Serializacion pickle json, marshal
Base64 base64 base64
Abrir URL en web browser webbrowser browsers
Async asyncio asyncdispatch, asyncfile, asyncnet, asyncstreams
Unittests unittests unittest
Diff difflib diff
Colores colorsys colors
MD5 hashlib.md5 md5
SHA1 hashlib.sha1 sha1
Servidor HTTP http.server asynchttpserver
Lexer shlex lexbase
Multi-Hilos threading threadpool
URL & URI urllib.parse uri
CSV csv parsecsv
Argumentos de linea de comando argparse parseopt
SMTP smtplib smtp
Galletitas de HTTP http.cookies cookies
Estadisticas statistics stats
Recorte de Texto textwrap wordwrap
Registro de Windows winreg registry
POSIX posix posix, posix_utils
SSL ssl openssl
CGI cgi cgi
Parsear JSON json parsejson, json
Parsear INI configparser parsecfg
Parsear XML xml parsexml, xmltree
Parsear HTML html.parser htmlparser
Parsear SQL parsesql
Colores en la Terminal terminal
Detectar Distro Linux distros
Generador HTML htmlgen
Azucares de Sintaxis sugar
JavaScript & Frontend dom, asyncjs, jscore, jsffi

Tuples

Tuplas son capacidad fija, comienzan en index 0, pueden contener tipos mixtos, pueden ser anonimas o nombrada, named tuple no tiene costo extra sobre anonymous tuple.

Anonymous Tuple

(1, 2, 3)

⬆️ Python ⬆️          ⬇️ Nim ⬇️

(1, 2, 3)

Named Tuple

  • Keys con nombre, Tupla sin nombre. Python NamedTuple requiere import collections.
collections.namedtuple("_", "key0 key1")("foo", 42)

⬆️ Python ⬆️          ⬇️ Nim ⬇️

(key0: "foo", key1: 42)

Named Tuple

  • Keys con nombre, Tupla con nombre. Python NamedTuple requiere import collections.
collections.namedtuple("NameHere", "key0 key1")("foo", 42)

⬆️ Python ⬆️          ⬇️ Nim ⬇️

type NameHere = tuple[key0: string, key1: int]
var variable: NameHere = (key0: "foo", key1: 42)

Tuplas Nim son similares a las Python NamedTuple.

Ver manual para mas acerca de tuplas.

Lists

Nim sequencias no son de capacidad fija, pueden crecer y achicarse, comienzan en indice 0 y pueden contener elementos del mismo Tipo.

["foo", "bar", "baz"]

⬆️ Python ⬆️          ⬇️ Nim ⬇️

@["foo", "bar", "baz"]

List Comprehensions

variable = [item for item in (-9, 1, 42, 0, -1, 9)]

⬆️ Python ⬆️          ⬇️ Nim ⬇️

let variable = collect(newSeq):
  for item in @[-9, 1, 42, 0, -1, 9]: item

La Comprension puede ser asignada a const tambien, y correra en tiempo de compilacion.

La comprehension es implementada con macro que es expandido en tiempo de compilacion, podes ver la expansion de codigo usando la opcion de compilador --expandMacro en la terminal:

let variable =
  var collectResult = newSeq(Natural(0))
  for item in items(@[-9, 1, 42, 0, -1, 9]):
    add(collectResult, item)
  collectResult

Comprehension puede ser anidada, multi-linea, multi-expresion, costo cero:

import sugar

let values = collect(newSeq):
  for val in [1, 2]:
    collect(newSeq):
      for val2 in [3, 4]:
        if (val, val2) != (1, 2):
          (val, val2)
        
assert values == @[@[(1, 3), (1, 4)], @[(2, 3), (2, 4)]]

One-liner:

print([i for i in range(0, 9)])

⬆️ Python ⬆️          ⬇️ Nim ⬇️

echo(block: collect newSeq: (for i in 0..9: i))

Python Comprehension convierte el codigo en un Generator, pero Nim Comprehension no convierte el codigo en un Iterator:

import sugar

func example() =
  discard collect(newSeq):
    for item in @[-9, 1, 42, 0, -1, 9]: 
      if item == 0: return
      item

example()

⬆️ Nim ⬆️          ⬇️ Python ⬇️

def example():
  [item for item in [-9, 1, 42, 0, -1, 9] if item == 0: return]
      
example()

Python se queja:

SyntaxError: invalid syntax.

Codigo que no funciona en Python funcionara en Nim, cosas como return, etc por que el codigo es silenciosamente convertido a un Generador por Python, pero es expandido a codigo normal por Nim.

  • Que es collect()?.

collect() toma de argumento lo que sea que tu Tipo de retorno usa como constructor.

Dict Comprehensions

variable = {key: value for key, value in enumerate((-9, 1, 42, 0, -1, 9))}

⬆️ Python ⬆️          ⬇️ Nim ⬇️

let variable = collect(initTable(4)):
  for key, value in @[-9, 1, 42, 0, -1, 9]: {key: value}

Set Comprehensions

variable = {item for item in (-9, 1, 42, 0, -1, 9)}

⬆️ Python ⬆️          ⬇️ Nim ⬇️

let variable = collect(initHashSet):
  for item in @[-9, 1, 42, 0, -1, 9]: {item}

Sets

Lenguaje Set Set Ordenado Bitset Bit Fields Imports
🐍 Python set()
👑 Nim HashSet() OrderedSet() set Bit Fields import sets
  • Python Set puede ser reemplazado con HashSet.

Python Set no es igual a Nim Set, el Set "por defecto" es un Bitset, que por cada posible valor contenido en el set, guarda 1 Bit indicando si el valor esta presente, entonces deberias usarlo para valores finitos limitados a un rango de posibles valores, si los valores posibles son conocidos en tiempo de compilacion, podes crear un Enum para los valores.

El numero entero mas grande que entra en un Set por defecto es 65535 equivalente a high(uint16).

Podes usar valores mas grandes usando un Subrange, si no necesitas valores mas chicos, un ejemplo stresando un set para poner 2_147_483_647 equivalente a high(int32) en un set en tiempo de compilacion:

const x = {range[2147483640..2147483647](2147483647)}
assert x is set  # Equals to {2147483647}

Los Set de Python no son iguales a los Set de Nim. Se debe saber el tipo de los valores que van en el Set y deben ser finitos. Podes imitar el Set de Python usando un HashSet. El Set de Nim es mas rapido y es muy eficiente en memoria. De hecho, el Set de Nim esta implementado con un Bit Vector, y HashSet esta implementado como dicionario. Para simples Tipos bandera y sets matematicos, usa Set.

Dictionaries

Tablas son como los diccionarios Python.

Lenguaje Diccionario Diccionario Ordenado Contador Import
🐍 Python dict() OrderedDict() Counter() import collections
👑 Nim Table() OrderedTable() CountTable() import tables

Table Constructors

dict(clave="valor", otro="cosas")

⬆️ Python ⬆️          ⬇️ Nim ⬇️

to_table({"clave": "valor", "otro": "cosas"})

Ordered Dictionary

collections.OrderedDict([(8, "hp"), (4, "laser"), (9, "motor")])

⬆️ Python ⬆️          ⬇️ Nim ⬇️

to_ordered_table({8: "hp", 4: "laser", 9: "motor"})

Counters

collections.Counter(["a", "b", "c", "a", "b", "b"])

⬆️ Python ⬆️          ⬇️ Nim ⬇️

to_count_table("abcabb")

Examples:

import tables

var diccionario = to_table({"hola": 1, "ahi": 2})

assert diccionario["hola"] == 1
diccionario["hola"] = 42
assert diccionario["hola"] == 42

assert len(diccionario) == 2
assert diccionario.has_key("hola")

for clave, valor in diccionario:
  echo clave, valor

Tablas son solamente azucar (syntax sugar) sobre un array de tuplas:

assert {"key": "value", "k": "v"} == [("key", "value"), ("k", "v")]
assert {"key": true, "k": false} == @[("key", true),  ("k", false)]

B-Tree Tables

B-Tree Table usando la misma API.

Ver:

Ternary operators

"resultado0" if condicional else "resultado1"

⬆️ Python ⬆️          ⬇️ Nim ⬇️

if condicional: "resultado0" else: "resultado1"

Como habras notado el Operador Ternario es simplemente un if..else en-linea.

Reading and writing files

Leer archivos linea por linea

with open("elarchivo.txt", "r") as f:
    for line in f:
        print(line)

⬆️ Python ⬆️          ⬇️ Nim ⬇️

for linea in lines("elarchivo.txt"):
  echo linea

Leer y escribir archivos:

write_file("elarchivo.txt", "esto simula datos")
assert read_file("elarchivo.txt") == "esto simula datos"

Leer archivos en tiempo de compilacion:

const constante = static_read("elarchivo.txt")  # Retorna un string en tiempo de compilacion

Change File Permissions

Cambiar permisos a archivos.

import os
os.chmod("file.txt", 0o777)

⬆️ Python ⬆️          ⬇️ Nim ⬇️

import filepermissions
chmod "file.txt", 0o777

Ejemplo asume que un archivo llamado "file.txt" existe. Usa permisos tipo Unix (Octal Unix permissions).

Ver https://nim-lang.github.io/fusion/src/fusion/filepermissions.html

Temporarily Change Folder

Cambiar de carpeta temporalmente, y volver.

import os

class withDir:
    # Unsafe without a __del__()

    def __init__(self, newPath):
        self.newPath = os.path.expanduser(newPath)

    def __enter__(self):
        self.savedPath = os.getcwd()
        os.chdir(self.newPath)

    def __exit__(self, etype, value, traceback):
        os.chdir(self.savedPath)


with withDir("subcarpeta"):
  print("Adentro de subcarpeta")
print("Volver fuera de subcarpeta")

⬆️ Python ⬆️          ⬇️ Nim ⬇️

import scripting

withDir "subcarpeta":
  echo "Adentro de subcarpeta"
echo "Volver fuera de subcarpeta"

Ver https://nim-lang.github.io/fusion/src/fusion/scripting.html

Map & Filter

def isPositive(arg: int) -> bool:
  return arg > 0

map(isPositive, [1, 2,-3, 5, -9])
filter(isPositive, [1, 2,-3, 5, -9])

⬆️ Python ⬆️          ⬇️ Nim ⬇️

proc esPositivo(arg: int): bool =
  return arg > 0

echo map([1, 2,-3, 5, -9], esPositivo)
echo filter([1, 2,-3, 5, -9], esPositivo)

Lambdas

variable: typing.Callable[[int, int], int] = lambda var1, var2: var1 + var2

⬆️ Python ⬆️          ⬇️ Nim ⬇️

var variable = proc (var1, var2: int): int = var1 + var2

Multi-linea:

var anon = func (x: int): bool =
             if x > 0:
               result = true
             else: 
               result = false

assert anon(9)

Funciones Anonimas en Nim es basicamente una funcion sin nombre.

Decorators

  • Templates y Macros pueden ser usados como Decoradores Python.
def decorator(argument):
  print("This is a Decorator")
  return argument

@decorator
def function_with_decorator() -> int:
  return 42

print(function_with_decorator())

⬆️ Python ⬆️          ⬇️ Nim ⬇️

template decorador(argumento: untyped) =
  echo "Esto imita un Decorador"
  argumento

func funcion_con_decorador(): int {.decorador.} =
  return 42

echo funcion_con_decorador()
  • Por que Nim no usa @decorator?.

Nim usa {. y .} por que puede tener muchisimos decoradores juntos.

Ademas los de Nim funcionan en variables y Tipos:

func funcion_con_decorador(): int {.discardable, inline, compiletime.} =
  return 42

let variable {.compiletime.} = 1000 / 2

type Colores {.pure.} = enum Rojo, Verde, Azul

JSON

Python usa strings multi-linea con JSON adentro, Nim usa JSON literal directamente en el codigo.

import json

variable = """{
    "key": "value",
    "other": true
}"""
variable = json.loads(variable)
print(variable)

⬆️ Python ⬆️          ⬇️ Nim ⬇️

import json

var variable = %*{
  "key": "value",
  "other": true
}
echo variable
  • %* convertira todo dentro de los corchetes a JSON, JSON es de Tipo JsonNode.
  • %* puede tener variables y literales adentro.
  • JSON puede tener comentarios adentro de %*, comentarios de Nim.
  • Si el JSON no es JSON valido el codigo no compila.
  • JsonNode puede ser muy util en Nim por que es un Tipo que puede tener Tipos mezclados y crecer/achicarse.
  • Podes leer JSON en tiempo de compilacion, y guardarlo en una constante.
  • Para parsear JSON desde string usa parseJson("{}").
  • Para parsear JSON desde archivo usa parseFile("file.json").
  • Documentacion de JSON

Self-Execution of Main Module

if __name__ == "__main__":
  main()

⬆️ Python ⬆️          ⬇️ Nim ⬇️

when is_main_module:
  main()

Unittests

import unittest

def setUpModule():
    """Setup: Ejecuta una sola vez antes de todos los tests."""
    pass

def tearDownModule():
    """Teardown: Ejecuta una sola vez despues de todos los tests."""
    pass


class TestName(unittest.TestCase):

    """Nombre del Test"""

    def setUp(self):
        """Setup: Ejecuta una sola vez antes de cada test."""
        pass

    def tearDown(self):
        """Teardown: Ejecuta una sola vez despues de cada test."""
        pass

    def test_ejemplo(self):
        self.assertEqual(42, 42)


if __name__ == "__main__":
    unittest.main()

⬆️ Python ⬆️          ⬇️ Nim ⬇️

import unittest

suite "Nombre del Test":

  echo "Setup: Ejecuta una sola vez antes de todos los tests."

  setup:
    echo "Setup: Ejecuta una sola vez antes de cada test."

  teardown:
    echo "Teardown: Ejecuta una sola vez despues de cada test."

  test "ejemplo":
    assert 42 == 42

  echo "Teardown: Ejecuta una sola vez despues de todos los tests."

Assert with customized messages

  • assert puede tomar como argumento un block, podes personalizar el mensaje para una mejor experiencia de usuario:
let a = 42
let b = 666
doAssert a == b, block:
  ("\nCustom Error Message!:" &
   "\n  a equals to " & $a &
   "\n  b equals to " & $b)

Testament

Alternativa a unittest, preparado para proyectos grandes.

DocStrings

DocStrings en Nim son comentarios en ReSTructuredText y MarkDown comenzando con ##, ReSTructuredText y MarkDown pueden estar mezclados juntos si queres.

Genera HTML, Latex (PDF) y JSON desde el codigo fuente Nim con nim doc archivo.nim.

Nim puede generar un grafico de dependencias internas DOT .dot con nim genDepend archivo.nim.

Podes ejecutar la documentation como si fueran Unittests con runnableExamples.

""" Documentation of Module """

class Kitten(object):
    """ Documentation of Class """
    age: int

    def purr(self):
        """ Documentation of function """
        print("Purr Purr")

⬆️ Python ⬆️          ⬇️ Nim ⬇️

## Documentacion del Modulo *ReSTructuredText* y **MarkDown**

type Gatito = object ## Documentacion del Tipo *ReSTructuredText* y **MarkDown**
  edad: int  ## Documentacion del Atributo *ReSTructuredText* y **MarkDown**

proc purr(self: Gatito) =
  ## Documentacion de Funcion *ReSTructuredText* y **MarkDown**
  echo "Miau Miau"

Optional Indentation

Para lineas cortas, podes escribir el codigo en una sola linea.

let a = try: 1 + 2 except: 42 finally: echo "Inline Try"

let b = if true: 2 / 4 elif false: 4 * 2 else: 0

for i in 0..9: echo i

proc foo() = echo "Funcion"

(proc   () = echo "Funcion Anonima")()

template bar() = echo "Template"

macro baz() = echo "Macro"

var i = 0
while i < 9: i += 1

when is_main_module: echo 42

CamelCase

  • Por que Nim es CamelCase en vez de snake_case?.

Realmente no es, Nim es Agnostico de Estilos.

let camelCase = 42      # Declara como camelCase
assert camel_case == 42 # Usa como snake_case

let snake_case = 1      # Declara como snake_case
assert snakeCase == 1   # Usa como camelCase

let `free style` = 9000
assert free_style == 9000

Esta caracteristica le permite a Nim interoperar transparentemente con muchisimos lenguajes de programacion con diferentes estilos.

Para un codigo mas homogeneo existe una forma por defecto, y puedes inclusive forzarla si quieres, para forzar la forma por defecto en el estilo de codigo puedes agregar al comando de compilacion --styleCheck:hint, Nim va a chequear estilos para tu codigo antes de compilar, similar a pycodestyle o black de Python, si quieres aun mas estricto usa --styleCheck:error. Nim viene con un auto-formateador de codigo llamado Nimpretty.

Muchos lenguajes de programacion tienen algun tipo de Case Insensitivity, como: PowerShell, SQL, PHP, Lisp, Assembly, Batch, ABAP, Ada, Visual Basic, VB.NET, Fortran, Pascal, Forth, Cobol, Scheme, Red, Rebol.

Si estas recien comenzando desde cero, podes usar los estilos como en Python para todo, no produce error por hacerlo.

def Vs proc/func

  • Por que Nim no usa def en vez de proc?.

Nim usa proc para funciones del nombre "Procedimiento" (Procedure).

Usa func para Programacion Funcional Libre de side-effects funciones de "funcion matematica".

Nim tiene seguimiento de side-effects.

No se puede usar echo dentro de func, por que echo muta stdout, es un Side-Effect, usa debugEcho.

Si estas recien comenzando desde cero, podes usar proc para todo, no produce error por hacerlo.

Async

Nim tiene Async integrado desde hace mucho tiempo, funciona como esperas con async, await, Future, etc.

asyncdispatch es el modulo para escribir codigo concurrente usando sintaxis async/await.

Future es un Tipo (como Future en Python, como Promise en JavaScript).

{.async.} es el Pragma que transforma funciones a Async (como async def en Python).

Hagamos el Hola Mundo official de Python Asyncio en Nim:

async def main():
    print("Hello ...")
    await asyncio.sleep(1)
    print("... World!")

asyncio.run(main())

⬆️ Python ⬆️          ⬇️ Nim ⬇️

proc main() {.async.} =
  echo("Hello ...")
  await sleep_async(1)
  echo("... World!")

wait_for main()

Internamente Async esta implementado usando metaprogramacion (Macros, Templates, Pragmas, etc).

Descripcion asyncCheck waitFor await
Espera que el Future este completo ✔️ ✔️
Ignora el Future ✔️
Retona el resultado dentro del Future ✔️ ✔️
Solo disponible dentro de async ✔️
  • Por que Nim no usa async def?.

Async es simplemente un macro en Nim, no hay necesidad de cambiar la sintaxis de todo el lenguaje, es como un Decorator en Python.

Ademas la misma funcion puede ser Async Y Sync al mismo tiempo, con el mismo codigo, con el mismo nombre.

En Python cuando tenes una libreria "foo", tal vez tenes foo (Sync) y aiofoo (Async), usualmente proyectos, repos, devs, APIs completamente diferentes, esto no es necesario en Nim, gracias a esa caracteristica.

Ver tambien asyncfile, asyncnet, asyncstreams, asyncftpclient, asyncfutures.

Do I have to know C?

Nunca tenes realmente que manualmente editar C, de la misma manera que en Python nunca tenes que manualmente editar PYC.

En Nim programas escribiendo Nim, de la misma manera que en Python programas escribiendo Python.

Templates

Templates reemplazan su invocacion con su contenido en tiempo de compilacion.

Imagina que el compilador va a copiar&pegar un pedazo de codigo por vos.

Template permite tener construcciones como funcion pero sin costos extra de performance, permite partir funciones gigantes en construcciones mas diminutas.

Demasiados nombres de funciones y variables pueden contaminar y saturar el namespace local, variables dentro de templates no existen afuera del template, templates no existen en el namespace en tiempo de ejecucion (si no lo exportas), templates pueden optimizar ciertos valores si son conocidos en tiempo de compilacion.

Templates no pueden hacer import or export de librerias automaticamente implicitamente, templates no pueden hacer "auto-import" de simbolos usandos dentro de si mismo, si usas alguna libreria importada en el cuerpo de un template, debes importar esa libreria cuando llamas ese template.

Adentro de los templates no se puede usar return por que no es una funcion.

Templates te permiten implementear una bonita API de muy alto nivel para uso habitual, mientras mantienen las cosas de optimizaciones y de bajo nivel fuera de tu cabeza y DRY.

Python with open("file.txt", mode = "r") as file: implementado usando 1 template:

Template explanation animation

GIF no es perfecto, pero es una aproximacion simplificada!.

Esta no es la manera de leer y escribir archivos en Nim, es solo un ejercicio.

Template no es perfecto, pero es una aproximacion simplificada!, ejercicio para el lector el tratar de mejorarlo ;P

template with_open(name: string, mode: char, body: untyped) =
  let flag = if mode == 'w': fmWrite else: fmRead  # "flag" No existe fuera de este template
  let file {.inject.} = open(name, flag)   # Crea e injecta variable "file", "file" existe fuera de este template por {.inject.}
  body                                     # El codigo "body" es pasado como argumento
  file.close()                             # Codigo luego de body

with_open("testin.nim", 'r'): # Imita Python with open("file", mode='r') as file
  echo "Hello Templates"      # Codigo dentro del template, estas 2 lineas son "body" del template
  echo file.read_all()        # Esta linea usa la variable "file"

Si estas recien comenzando desde cero, podes usar Funciones como en Python para todo, no produce error por hacerlo.

How to share variables between functions?

Compartir variables entre funciones es similar a Python.

Variable Global:

global_variable = ""

def function0():
    global global_variable
    global_variable = "cat"

def function1():
    global global_variable
    global_variable = "dog"

function0()
assert global_variable == "cat"
function1()
assert global_variable == "dog"
function0()
assert global_variable == "cat"

⬆️ Python ⬆️          ⬇️ Nim ⬇️

var global_variable = ""

proc function0() =
  global_variable = "cat"

proc function1() =
  global_variable = "dog"

function0()
assert global_variable == "cat"
function1()
assert global_variable == "dog"
function0()
assert global_variable == "cat"

Object Attribute:

class IceCream:

  def __init__(self):
    self.object_attribute = None

def function_a(food):
    food.object_attribute = 9

def function_b(food):
    food.object_attribute = 5

food = IceCream()
function_a(food)
assert food.object_attribute == 9
function_b(food)
assert food.object_attribute == 5
function_a(food)
assert food.object_attribute == 9

⬆️ Python ⬆️          ⬇️ Nim ⬇️

type IceCream = object
  object_attribute: int

proc functiona(food: var IceCream) =
  food.object_attribute = 9

proc functionb(food: var IceCream) =
  food.object_attribute = 5

var food = IceCream()
functiona(food)
assert food.object_attribute == 9
functionb(food)
assert food.object_attribute == 5
functiona(food)
assert food.object_attribute == 9

Podes pasar funciones como argumento de funciones como en Python tambien.

Import Nim files on Python

Python Syntax for Nim

Publish to PYPI

Silent Compilation

Si queres la compilacion completamente silenciosa (vas a perder errores y consejos importantes), podes agregar al comando de compilacion --hints:off --verbosity:0.

Compiler Help

La ayuda del compilador es larga, para hacerla mas amigable al usuario solo las cosas mas frecuentes se muestran con --help, para ver la ayuda completa usa --fullhelp.

Build Modes

Cuando tu codigo esta listo para produccion debes usar el Release build, podes agregar al comando de compilacion -d:release.

Caracteristica Release Build Debug Build
Velocidad Rapido Lento
Archivo Chico Grande
Optimizado ✔️
Tracebacks ✔️
Chequeos Run-time ✔️ ✔️
Chequeos Compile-time ✔️ ✔️
assert ✔️
doAssert ✔️ ✔️

MicroPython

Nim compila a C, puede correr en Arduino y hardware similar.

Nim tiene varios tipos de manejo de memoria para satisfacer tu necesidad, incluido manejo de memoria manual. Binarios Nim son chicos cuando se compilan en Release Build y pueden entrar en poco espacio de disco.

SuperCollider

SuperCollider es C++ entonces puede ser re-utilizado con Nim.

Teoricamente, plugins Nim de SuperCollider son rapidos como codigo C. La metaprogramacion de Nim permite hacer DSL amigables para LiveCoding.

Algunos proyectos para Nim LiveCoding:

ABC

Ver

⬆️ ⬆️ ⬆️ ⬆️

Clone this wiki locally