Adrián Arroyo Calle

Hace unos pocos días Microsoft Research, la parte de investigación de Microsoft, publicó Bosque, un nuevo lenguaje de programación. El lenguaje sigue en fase experimental y el compilador es un poco patatero (está hecho en TypeScript y necesita Node.js para funcionar). Pero eso es lo de menos, porque son detalles. Lo importante son las ideas que aporta.

La filosofía de Bosque

Como muchos otros lenguajes, Bosque nace de unas ideas que pretende seguir. La filosofía detrás de Bosque es la siguiente:

El lenguaje de programación Bosque ha sido diseñado para escribir código que es simple, obvio y fácil de razonar con él tanto para humanos como para máquinas. Las características claves del diseño proporcionan formas de evitar la complejidad accidental en el proceso de desarrollo. El objetivo es mejorar la productividad de los desarrolladores, mejorar la calidad del software y habilitar una nueva clase de compiladores y herramientas de desarrollo.

En su descripción además nos informa que toma conceptos de ML (el padre de Haskell) y JavaScript, además el autor, Mark Marron, también ha admitido que hay algunas cosas de P que le gustan.

Hola Mundo en Bosque

Actualmente el compilador está en fase experimental, si quieres probarlo estas son los comandos necesarios para hacerlo funcionar en Linux (Node.js tiene que estar instalado)

git clone https://github.com/Microsoft/BosqueLanguage cd BosqueLanguage/ref_impl npm install npm run-script build npm test node bin/test/app_runner.js FICHERO_BOSQUE

Y un hola mundo tiene la siguiente pinta.

namespace NSMain; entrypoint function main(): String { return "Hola Mundo"; }

Y el resultado es el siguiente:

A primera vista ya parece un poco raro ya que hemos devuelto directamente un string en la función main. En Bosque no hay efectos colaterales, lo que también implica que no puede haber (de momento) entrada y salida más allá de las propias funciones de punto de entrada. En Bosque el punto de entrada es arbitrario, pero de momento la mayoría de ejemplos usan la función main dentro de NSMain.

Variables inmutables

En Bosque todas las variables son inmutables. Eso quiere decir que no es posible modificar su contenido una vez han sido asignadas:

namespace NSMain; function add(x: Int, y: Int): Int { return x+y; } entrypoint function main(): Int { var result = add(7,12); return result; }

No obstante, es posible usar azúcar sintáctico y tener variables "mutables" con var!, la gracia es que debido a otros diseños de Bosque que veremos más adelante, no altera el resultado global.

Parámetros por referencia

También es posible modificar ciertos parámetros por referencia directamente de forma segura, aseguranda la ausencia de efectos colaterales. Esto permite una programación más cómoda.

Tipado de Strings

Los diseñadores de Bosque saben que el tipo String es muy versátil y se usa para muchas cosas, pero para el compilador sigue siendo String. ¿Por qué no tipar estos strings? En un ejemplo sacado de la documentación, se usa Zipcode como un tipo de String. Los zipcodes solo pueden pasar a string y un string no puede pasar directamente a Zipcode si no es verificado antes. Así evitamos mezclar Zipcodes y Emails (por ejemplo), que en otros lenguajes serían ambos Strings.

function foo(zip: String[Zipcode], name: String) {...} var zc: String[Zipcode] = ...; var user: String = ...; foo(user, zc) //Type error String not convertible to String[Zipcode] foo(zc, user) //ok

Invocaciones flexibles

Aquí de forma parecida a Python, Bosque permite gran flexibilidad a la hora de pasar argumentos.

function nsum(d: Int, ...args: List[Int]): Int { return args.sum(default=d); } function np(p1: Int, p2: Int): {x: Int, y: Int} { return @{x=p1, y=p2}; } //calls with explicit arguments var x = nsum(0, 1, 2, 3); //returns 6 var a = np(1, 2); //returns @{x=1, y=2} var b = np(p2=2, 1); //also returns @{x=1, y=2} //calls with spread arguments var t = @[1, 2, 3]; var p = nsum(0, ...t); //returns 6 -- same as explicit call var r = @{p1=1, p2=2}; var q = np(...r); //returns @{x=1, y=2} -- same as explicit call

Operaciones de golpe

Bosque permite hacer modificaciones sobre una estructura o tupla de golpe, en una sola línea, indicando todos los campos que se modifican y su operación. Esto puede ser muy interesante si permite generar instrucciones SIMD automáticamente y aprovechar de una vez toda esa potencia subyacente que tienen los procesadores actuales.

Manejo de nones

En Bosque existe el valor especial none similar al NULL o undefined de otros lenguajes. Bosque tiene un tratamiento especial para este tipo de valores que permite que sea más cómodo trabajar con ellos. La verdad es que esto me ha sorprendido ya que la tendencia actual es a reducir los NULL. Aunque claro, si se permiten solamente en situaciones controladas por el compilador podría ser muy interesante, ya que sería más simple y sencillo que las alternativas que existen en Haskell y Rust.

Procesamiento iterativo

Bosque no tiene bucles. No al menos nada comparable al WHILE y al FOR. Otro trabajo de investigación de Microsoft (Mining Semantic Loop Idioms) afirmó que en la gran mayoría de casos todos los bucles pertenecen a unos casos concretos de bucle. Es por ello, y con el objetivo de hacer la programación más predecible, que no existen los bucles como tal. Otra vez, un ejemplo desde la documentación

var v: List[Int?] = List@{1, 2, none, 4}; //Chained - List@{1, 4, 16} v->filter(fn(x) => x != none)->map[Int](fn(x) => x*x) //Piped none filter - List@{1, 4, 16} v |> filter(fn(x) => x != none) |> map[Int](fn(x) => x*x) //Piped with noneable filter - List@{1, 4, 16} v |??> map[Int](fn(x) => x*x) //Piped with none to result - List@{1, 4, none, 16} v |?> map[Int](fn(x) => x*x)

Recursión

En la mayoría de lenguajes funcionales se suelen suplir algunas de las carencias habituales (bucles, variables inmutables, etc) con complejas jerarquías de recursión. Estos sistemas funcionan pero no son muy claros. Bosque quiere romper con este esquema permitiendo solo llamadas recursivas a funciones que explícitamente lo marquen.

Igualdad

Las comprobaciones de igualdad siempre están definidas por el tipo. Nunca se comprueba la referencia, evitando así uno de los tipos de fallos más comunes en lenguajes OOP.

Y muchas otras cosas. El objetivo final es que cada programa Bosque tenga un único y bien definido resultado, y cada función lo mismo. De este modo será más fácil encontrar y razonar sobre los fallos, obteniendo software de mejor calidad en menos tiempo.

Habrá que ver como evoluciona Bosque, que ahora mismo está muy verde y sujeto a muchos cambios. Puede ser una interesante alternativa en el futuro o un simple experimento de Microsoft Research que no vaya a más (o cuyos resultados se apliquen a C# por ejemplo). Vosotros, ¿qué opináis?