AssemblyScript

AssemblyScript es un lenguaje de programación (a modo de “script”) que puede verse como un superconjunto de Javascript o una variante de Typescript, con tipos de datos estrictos y es útil para generar WebAssembly (wasm).

Esta es una referencia ágil para quién tenga nociones de programación o codifique con algún otro lenguaje, también a modo de repaso y uso frecuente. Siendo así, tener esta información como memoria te resultará simple de acceder a los fundamentos, incluso como prueba de concepto sobre el lenguaje.

Ejemplo esencial

function hi(): void {
    console.log("Hi there!");
}

Si conoces javascript puedes notar que la diferencia consiste en que se indica el tipo de la función, en este caso es vacío y se usa void.

Tips esenciales del lenguaje

Para quien tenga habilidades, experticia en programación y/o requiera agilidad en conceptos técnicos, pueden resumirse los siguientes tips esenciales del lenguaje:

  1. Tipos de datos básicos: i32, f32, bool, string, auto. Las variables se definen simplemente con let y la asignación del valor.
  2. Las funciones se definen con function, luego los parámetros van entre paréntesis (...), continuando con el nombre del parámetro, el caracter : que separa el tipo de datos posteriormente (y separando los parámetros con coma ,). El tipo de datos a retornar va también después de :. Además, se usa return para retornar un valor. También existe una alternativa moderna de expresar funciones (llamada funciones de flecha) que omite la palabra function y en su lugar se usa: () => {...}.
  3. Para el bloque de la función o el flujo de control se usan llaves {}. Las sentencias deben terminar siempre con punto y coma ;.
  4. El flujo de control es semejante a lenguajes como C, Java, Kotlin, Javascript, es decir que se cuenta con una anatomía cercana para el uso de if, for, while, incluso para el manejo de excepciones (try).
  5. El constructor de una clase lleva el nombre constructor y la clase se define con la palabra reservada class y el nombre.
  6. A diferencia de otros lenguajes, el uso de JSON es nativo del lenguaje (siendo básicamente la mezcla entre array y object). Para esto se cuenta con las funciones JSON.stringify y JSON.parse (heredadas de Javascript).

Tipos de datos básicos

Tipo Descripción
string Cadena de caracteres o texto
i32 Número entero signado a 32-bit
i64 Número entero signado a 64-bit
u32 Número entero sin signo a 32-bit
u64 Número entero sin signo a 64-bit
f32 Número flotante a 332-bit
f64 Número flotante a 64-bit
bool Booleano
auto Para casos en donde puede aplicar varios tipos
void vacío (para métodos)

A diferencia de Typescript el tipo de datos number no existe y se presentan diferentes tipos.

Declaración de variables

Se puede declarar variables con las palabras reservadas let y const, esta última para indicar que el valor no cambia o no es mutable.

Ejemplo:

let variable: string = "Ana";
let list: Array<string> = ["a", "b", "c"];
let age: i32: 25;
const inmutable = "Ana";

Definición de funciones

function plus1 (a: i64, b: i64): i64 {
    return a + b;
}

const plus2 = (a: i64, b: i64) => a + b;

Comentarios

// Esto es un comentario de fin de línea

/*
   Este es un comentario de bloque
*/

Condicionamiento if / else (if)

if (i == 1) {
    console.log('one')
}
else if (i == 2) {
    console.log('two')
}
else {
    console.log('aha')
};

El Ciclo For

for (let index = 0; index < list.length; index++) {
    console.log(index);
}
for (ñet item in list) {
    console.log(item);
}

Puede interrumpirse un ciclo con la sentencia break o dar el paso a la siguiente iteración con la setencia continue.

El Ciclo While

while (i < 10) {
    i++;
}
do {
    i++;
}
while (i < 10);

Puede interrumpirse un ciclo con la sentencia break

Evaluador Switch

switch (i) {
    case 1: console.log("one"); break;
    case 2: console.log("two"); break;
    default: console.log("aha");
}

Iteracion forEach

list.forEach(function (item) {
    console.log(item);
})

Excepciones

try {
    ...
}
catch (e) {
    ...
}
finally {
    ...
}

Puedes usar throw (por ejemplo: throw new Error('error!')) para forzar el lanzamiento de un error.

Clases

class Circle extends Shape {
    constructor (radius: Number) {
        this.radius = radius;
    }

    expand (n): Number {
        return super.expand(n) * Math.PI;
    }
}

Intefaces

interface PersonModel {
    name: string;
    age: i8;
}

Instalación del lenguaje

Para configurar un proyecto cras una carpeta, te ubicas dentro de ella, lugo abres una terminal y, teniendo Node.js 12+ instalado, ejecutas lo siguiente:

npm init -y
npm install --save @assemblyscript/loader
npm install --save-dev assemblyscript
npx asinit .

Puedes compilar el ejemplo por defecto ejecutando: npm run asbuild
Además, puedes probar el ejemplo ejecutando: npm test

Proyecto de inicio rapido

Una vez establecido el entorno con los comandos del tópico anterior, usaremos el archivo generado con nombre index.ts que se encuentra dentro de la carpeta assembly, el cual viene con la siguiente función:

export function add(a: i32, b: i32): i32 {
  return a + b;
}

La idea es obtener nuestra propia prueba de concepto mínima. Para ello crearemos un archivo app.js en la raíz del proyecto con el siguiente contenido:

const sum = require('./index.js').add;
console.log(sum(4, 5));

Para tener clara la estructura de archivos vistos veamos el siguiente esquema:

Para ver el resultado de nuestro programa ejecutamos:

node app.js

Para revisarlo desde un navegador, crearemos nuestra plantilla index.html en la raíz del proyecto, la cual tendrá el siguiente contenido:

<!DOCTYPE html>
<html lang="en">
<body>
    <p>Inspect "console" ...</p>
    <script>
        (async () => {
            const importObject = { env: {
                abort(_msg, _file, line, column) {
                    console.error(`wasm error at ${_file} ~> ${line},${column}: ${_msg}`);
                }
            }};

            const module = await fetch('./build/optimized.wasm')
                .then(res => res.arrayBuffer() )
                .then(bytes => WebAssembly.instantiate(bytes, importObject))
            const add = module.instance.exports.add;
            console.log('Sum =', add(2, 3));
        })();
    </script>
</body>
</html>

En lugar de WebAssembly.instantiate puede usarse WebAssembly.instantiateStreaming con el fetch como parámetro, es decir, establecer las líneas correspondientes al módulo de WebAssembly simplificándose así:

const module = await WebAssembly.instantiateStreaming(fetch('./build/optimized.wasm'), importObject);

Esto reduce la asignación de la constante module de tres líneas a una sóla.

Para probarlo en el navegador puede usarse algún servidor web local, sino instalar para Node.js módulos como http-server o serve. Veamos los comandos con serve.

npm install -g serve
serve 

Una vez se ejecute serve, puedes abrir un navegador con la dirección: localhost:5000


© 2021 by César Arcila