-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Nim for Python Programmers ES
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 |
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 |
---|---|---|---|---|
var |
❎ | ✔️ | ❎ | ❎ |
let |
❎ | ✔️ | ✔️ | ✔️ |
const |
✔️ | ❎ | ✔️ | ✔️ |
Si estas recien comenzando desde cero, podes usar var
para todo cuando estas aprendiendo, no produce error por hacerlo.
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 |
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
Si estas recien comenzando desde cero, podes usar los import como en Python para todo cuando estas aprendiendo, no produce error por hacerlo.
Para entender mejor, es recomendado leer (en Ingles): https://narimiran.github.io/2019/07/01/nim-import.html
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.
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.
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.
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
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()
paraobject
. -
newFoo()
pararef object
.
Lee la documentation para Nombrar cosas siguiendo las mejores practicas.
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
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]
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.
- Para una mejor documentacion ver: https://nim-lang.github.io/Nim/drnim.html
Python no tiene un Null Coalescing Operator (en ~2020).
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
Para un "With Context Manager" (Manejador de Contextos) en Nim tenes las siguientes opciones:
Lenguaje | String | String Multi-linea | String Crudo | String Crudo Multi-linea | Comilla | Siempre es Unicode |
---|---|---|---|---|---|---|
🐍 Python | "foo" |
"""foo""" |
r"foo" |
r"""foo""" |
" '
|
❎ |
👑 Nim | "foo" |
"""foo""" |
r"foo" |
r"""foo""" |
" |
✔️ |
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") |
- Las operaciones de string de Nim requieren
import strutils
. - Comparacion detallada.
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 |
- No es una lista completa solo un repaso rapido. Para mas info ver https://nim-lang.org/docs/lib.html
(1, 2, 3)
⬆️ Python ⬆️ ⬇️ Nim ⬇️
(1, 2, 3)
- Keys con nombre, Tupla sin nombre. Python NamedTuple requiere
import collections
.
collections.namedtuple("_", "key0 key1")("foo", 42)
⬆️ Python ⬆️ ⬇️ Nim ⬇️
(key0: "foo", key1: 42)
- 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. Las Tuplas Nim pueden tener Tipos mezclados.
Ver manual para mas acerca de tuplas.
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"]
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
- Que es
collect()
?.
collect()
toma de argumento lo que sea que tu Tipo de retorno usa como constructor.
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}
-
collect()
requiereimport sugar
.
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}
-
collect()
requiereimport sugar
.
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.
Tablas son como los diccionarios Python.
Lenguaje | Diccionario | Diccionario Ordenado | Contador | Import |
---|---|---|---|---|
🐍 Python | dict() |
OrderedDict() |
Counter() |
import collections |
👑 Nim | Table() |
OrderedTable() |
CountTable() |
import tables |
dict(clave="valor", otro="cosas")
⬆️ Python ⬆️ ⬇️ Nim ⬇️
to_table({"clave": "valor", "otro": "cosas"})
collections.OrderedDict([(8, "hp"), (4, "laser"), (9, "motor")])
⬆️ Python ⬆️ ⬇️ Nim ⬇️
to_ordered_table({8: "hp", 4: "laser", 9: "motor"})
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
"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.
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
- Documentacion de
lines()
https://nim-lang.org/docs/io.html#lines.i%2Cstring
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
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)
- Map y Filter requiere
import sequtils
.
variable: typing.Callable[[int, int], int] = lambda var1, var2: var1 + var2
⬆️ Python ⬆️ ⬇️ Nim ⬇️
var variable = (proc (var1, var2: int): int = var1 + var2)
Funciones Anonimas en Nim es basicamente una funcion sin nombre y rodeada de parentesis.
- 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
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 TipoJsonNode
. -
%*
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
if __name__ == "__main__":
main()
⬆️ Python ⬆️ ⬇️ Nim ⬇️
when is_main_module:
main()
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."
- Documentacion de Unittest.
- Nimble el manejador de paquetes puede tambien ejecutar Unittests.
- NimScript puede tambien ejecutar Unittests.
-
Podes ejecutar la documentation como si fueran Unittests con
runnableExamples
. - Testament es un ejecutador de Unittests mas avanzado.
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"
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
- 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
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
.
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.
- Por que Nim no usa
def
en vez deproc
?.
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.
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.
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 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:
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.
Compartir variables entre funciones es similar a Python.
Variable Global:
variable_global = ""
def funcion0():
variable_global = "gato"
def funcion1():
variable_global = "perro"
funcion0()
assert variable_global == "gato"
funcion1()
assert variable_global == "perro"
funcion0()
assert variable_global == "gato"
⬆️ Python ⬆️ ⬇️ Nim ⬇️
var variable_global = ""
proc funcion0() =
variable_global = "gato"
proc funcion1() =
variable_global = "perro"
funcion0()
assert variable_global == "gato"
funcion1()
assert variable_global == "perro"
funcion0()
assert variable_global == "gato"
Atributo de Objeto:
class IceCream(object):
atributo_de_objeto: int
def funciona(food: IceCream):
food.atributo_de_objeto = 9
def funcionb(food: IceCream):
food.atributo_de_objeto = 5
food = IceCream()
funciona(food)
assert food.atributo_de_objeto == 9
funcionb(food)
assert food.atributo_de_objeto == 5
funciona(food)
assert food.atributo_de_objeto == 9
⬆️ Python ⬆️ ⬇️ Nim ⬇️
type IceCream = object
atributo_de_objeto: int
proc funciona(food: var IceCream) =
food.atributo_de_objeto = 9
proc funcionb(food: var IceCream) =
food.atributo_de_objeto = 5
var food = IceCream()
funciona(food)
assert food.atributo_de_objeto == 9
funcionb(food)
assert food.atributo_de_objeto == 5
funciona(food)
assert food.atributo_de_objeto == 9
Podes pasar funciones como argumento de funciones como en Python tambien.
- https://github.com/yglukhov/nimpy/wiki#publish-to-pypi
- https://github.com/sstadick/ponim/blob/master/README.md#nim--python--poetry--
- https://github.com/sstadick/nython#nython
Si queres la compilacion completamente silenciosa (vas a perder errores y consejos importantes),
podes agregar al comando de compilacion --hints:off --verbosity:0
.
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
.
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 |
✔️ | ✔️ |
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.
- https://github.com/zevv/nim-arduino
- https://gitlab.com/endes123321/nimcdl/tree/master#nimcdl-nim-circuit-design-language
- https://github.com/cfvescovo/Arduino-Nim#arduino-nim
- https://gitlab.com/nimbed/nimbed#nimbed
- https://gitlab.com/endes123321/led-controller-frontend#led-controller-frontend
- https://ftp.heanet.ie/mirrors/fosdem-video/2020/AW1.125/nimoneverything.webm
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:
Intro
Getting Started
- Install
- Docs
- Curated Packages
- Editor Support
- Unofficial FAQ
- Nim for C programmers
- Nim for Python programmers
- Nim for TypeScript programmers
- Nim for D programmers
- Nim for Java programmers
- Nim for Haskell programmers
Developing
- Build
- Contribute
- Creating a release
- Compiler module reference
- Consts defined by the compiler
- Debugging the compiler
- GitHub Actions/Travis CI/Circle CI/Appveyor
- GitLab CI setup
- Standard library and the JavaScript backend
Misc