Todo lenguaje de programación define estructuras base que determinan la forma de escribir el lenguaje. El siguiente capítulo detalla las normas básicas con las que funciona Wren.
Todos los tipos de datos en Wren son objetos, instancias de una clase específica. Las operaciones son métodos disponibles en dichas clases.
Estas son las clases disponibles dentro del lenguaje Wren "vainilla".
-
Bool
-
Class
-
Fiber
-
Fn
-
List
-
Map
-
Null
-
Num
-
Object
-
Range
-
Sequence
-
String
-
System
-
Meta
-
Random
No se puede heredar desde estas clases debido al problema de Reentrancia (Wren no tiene esta capacidad). Para extender solo es posible usando la composición (Crear una nueva clase). Esto significa que al llamar a un método de una clase que herede de un tipo base, la máquina virtual no podrá diferenciar los métodos (Ya que son clases creadas con código de bajo nivel), lo que puede causar errores, por esta razón no está permitido heredar de las clases primitivas.
Estas clases son exclusivas de la Wren CLI. No estarán disponibles fuera del entorno de ejecución de este intérprete de Wren.
-
Directory
-
File
-
Stat
-
Stdin
-
Stdout
-
Platform
-
Process
-
Scheduler
-
Timer
Una variable es un contenedor de un valor específico. Este valor puede ser una cadena de caracteres, númerico, booleano u otro tipo de objeto.
var mensaje = "Hola mundo Wren"
System.print(mensaje)
Se puede modificar el valor posteriormente
var mensaje = "Hola mundo Wren"
System.print(mensaje)
mensaje = "El chercán es una bonita ave"
System.print(mensaje)
Si usamos una variable sin haberla declarado antes recibiremos un error similar a lo siguiente:
var mensaje = "Hola"
System.print(mensaj)
[repl line 1] Error at 'mensaj': Variable is used but not defined.
Debemos siempre procurar que todas nuestras variables estén declaradas dentro del contexto y que no existan errores de escritura en sus nombres.
Similar al Lenguaje C, para los indentificadores (nombres de variables, clases, metodos, funciones) se pueden utilizar los caracteres de la lista ascii
y comenzar con un caracter alfabético o guión bajo. Los identificadores en Wren diferencian entre mayúsculas y minúsculas. Solo se permiten letras (A - Z
, a - z
), números (0 - 9
) y guión bajo (_
). No se permiten espacios o comenzar con un número o guión alto (-
).
13hola
mi-variable
$miVariable
mi variable
ñandú
👨miMetodo
Mi👩clase
No están permitidos caracteres UTF-8 como la Ñ
o los emojis en los identificadores. Sin embargo las Strings
las soportan en su contenido sin problemas. Hay lenguajes como Swift o Emoji Code que si soportan identificadores con emojis, aunque la utilidad de esta práctica es debatible.
🏁 🍇
😀 🔤Hello World!🔤❗️
🍉
Un caso especial es para los identificadores con guión bajo como _color
(un guión bajo al principio) y __sabor
(dos guiones bajos al principio). Con un guión bajo indica que es una propiedad de instancia, mientras que con dos guiones bajos indican que es una propiedad de clase. Más detalles en la sección de Clases.
Para Wren es importante que las clases comiencen su nombre con mayúsculas. Si bien es posible definir clases con letras minúsculas no es recomendable hacerlo debido a que pueden colisionar con variables dentro del contexto de clase o método.
Ejemplos
// Asociamos la clase `Numero` como un substituto para llamar a la clase `Num`
var Numero = Num
// Dará error. No se puede heredar de las clases primitivas.
class Numero is Num {}
// Es posible nombrar con minúsculas, pero puede dar
// conflictos de contexto al momento de usarlo dentro
// de una clase. (Puede hacer colisión con nombres de variable)
class numero {}
Wren utiliza los saltos de línea (\n
), por lo que no es necesario utilizar el punto y coma (;
) para separar instrucciones. Sin embargo omite los saltos de línea si la instrucción espera más información para ser válida.
Los saltos de línea son considerados en la declaración de bloques de código. Por lo tanto es importante la posición de las llaves ("{}") dentro de una instrucción.
// Correcto
if (condiccion == true) { x = 0 }
// Correcto
if (condiccion == true) {
x = 0
}
// Correcto
if (condicion == true) x = 0
Condicionales de una sola línea. Tienen el formato de
condición
?
retorno si es verdadero
:
retorno si es falso
.
// Si la condición es verdadera, x será 0. Si es falsa, x será 1
var x = condicion ? 0 : 1
Utilizando el operador OR (||)
. Se puede asignar un valor predeterminado a una variable.
// Si x es nulo se asignará 0. Caso contrario mantiene su valor anterior.
x = x || 0
Si la llave no está presente en la misma línea, gatillará un error.
// Error
if (condiction == true)
{
x = 0
}
Al considerar los saltos de línea significativos, provoca un comportamiento inusual al momento de llamar métodos.
Los números pseudo aleatorios son generados utilizando la clase `Random`. Para generar un número del 0 al 9 se necesita utilizar 10, ya que el número máximo utilizado no está incluido dentro de la secuencia. Es decir se incluye 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 (total 10 números) dentro de los posibles resultados.
Instrucción en una sola línea.
Random.new().int(10)
Esto no es válido en Wren (Pero si es válido en la mayoría de los lenguajes de programación).
Random
.new()
.int(10)
Para ser válido necesitamos poner un punto al final de la línea.
Random.
new().
int(10)
Este comportamiento inusual espera ser reparado en la versión 0.4
.
Al tener instrucciones de una sola línea, Wren asume la existencia de una instrucción de retorno ("return"). Si no hay un valor, se asume que el valor de retorno es null.
class Auto {
// ruedas retornará siempre el valor "4"
ruedas {4}
// similar a ruedas, pero usando una instrucción "return" explícita
puertas {
return 2
}
}
Wren es un lenguaje simple y pequeño. Sus palabras reservadas son las siguientes:
break class construct else false for foreign if import
in is null return static super this true var while
Una palabra reservada tiene un significado especial para Wren, por lo que no se recomienda usarlas para llamar a variables o clases. De esta forma se evita conflictos y confusiones.
Las funciones en Wren son instancias de la clase Fn y deben ser llamadas utilizando su método call(). Al ser un objeto más, pueden ser pasadas como parámetros a métodos u otras funciones.
var mostrar = Fn.new {|parametro|
System.print(parametro)
}
// muestra: hola
mostrar.call("hola")
Son similares a las funciones, con la diferencia en que se ejecutarán en un hilo (thread) distinto. Además del método call() tienen un método try() usado normalmente para "atajar" errores.
var mostrar = Fn.new {|parametro|
System.print(parametro)
}
// Si existe un error se almacenará en la variable error
var error = Fiber.new { mostrar.call("hola") }.try()
Las clases se declaran utilizando la palabra clave class antes del nombre.
class MiClase {}
La herencia se define utilizando la palabra clave is. Solo se heredan los métodos de instancia. Propiedades de clase (_var) y de instancia (_var), constructores y métodos de clase (_static) no son heredados (todos son privados).
class Animal {
// la propiedad _nombre solo puede ser accesible
// a clases hijos si se definen como métodos accesadores
nombre {_nombre}
// creamos un mutador de la clase
nombre = (value) {
_nombre = value
}
imprimir() {
System.print(nombre)
}
}
class Perro is Animal {
imprimir() {
System.print("imprimir en hijo")
}
// constructores no son heredables
construct nuevo(nom) {
// llamamos al método mutador de la clase padre
this.nombre = nom
// Usamos `super` para llamar a un método de la clase padre
// Muestra "Joe" usando el método accesador del padre
super.imprimir()
// Muestra null
System.print(_nombre)
}
}
var perrito = Perro.nuevo("Joe")
Para definir un constructor se utiliza la palabra clave construct
.
Para llamar a la instancia se usa this
(opcional).
class Auto {
construct nuevo() {
System.print(this)
}
}
Para definir un método o propiedad de clase se utiliza la palabra clave static.
class Fruta {
// accesador (getter)
static cantidad {__cantidad}
// mutador (setter)
static cantidad = (value) {
// doble guión bajo indica propiedad de clase
__cantidad = value
}
// método de clase
static comer() {
cantidad = cantidad - 1
}
}
Para saber el padre de la clase se utiliza la propiedad supertype
.
Esta propiedad debe ser llamada recursivamente para obtener el árbol de herencia.
-
La clase primitiva base de todas es
Object
. -
No se puede heredar de los primitivos como
String
,Num
,Map
, entre otros.
class Animal {}
class Perro is Animal {}
System.print(Perro.supertype) // Animal
if (Perro.supertype == Animal) {
// Perro es animal
}
Si queremos saber el tipo de una instancia se puede usar is
.
var perrito = Perro.new()
if (perrito is Perro) {
// perrito es Perro
}
if (Perro is Animal) {
// Falso, ya que son clases distintas
}
-
Las propiedades
type
,supertype
yname
vienen dentro de cada clase. -
Object
es la única clase que no tienesupertype
.
class Animal {}
class Perro is Animal {}
System.print(Perro.name)
System.print(Perro.type)
System.print(Perro.supertype.name)
System.print(Perro.supertype.type)
System.print(Perro.supertype.supertype)
System.print(Object.supertype)
Existen las estructuras de control de flujo tradicionales: if, for, while y break.
if (condicion) {
// codigo
}
while (condicion) {
// codigo
}
for (elemento in secuencia) {
// codigo
break
}
Para separar el código en diversos archivos esta la instrucción import
.
El leer los archivos e importar su contenido dependerá del intérprete.
La instrucción import
va de la mano con la palabra for
.
// El módulo se llama "random" e importamos la clase "Random" que está dentro de este módulo
import "random" for Random
Es la clase padre de todas las demás clases en Wren.
Algunos métodos destacables:
-
toString
: Convierte el objeto a un String. -
type
: Retorna la clase a la cual el pertenece el objeto.
// Convertimos los datos a un String serializable
3.toString
["1",2,"tres", true].toString
// similar a "1" == "2", pero sin posibilidad de sobre escribir el operador
Object.same("1", "2")
// verificamos que la instancia pertenezca a la clase
if ("1" is String) {
System.print("hola")
}
Cuando se comienza a escribir código más elaborado, existe mayor necesidad de pensar como codificar las soluciones a los problemas. Una vez que se soluciona el problema, se dedicará una gran cantidad de tiempo en revisar y perfeccionar el algoritmo.
Los comentarios te permiten escribir en lenguaje humano como Inglés o Español, dentro de los programas.
Los comentarios en Wren utilizan la misma sintaxis que el Lenguaje de Programación C
.
Los símbolos son los siguientes: /* */
(multi línea) y //
(línea única).
// Comentario de una sola línea
/*
Este comentario
tiene múltiples
líneas
*/
Se pueden anidar los comentarios. útil para comentar código que ya tenga comentarios.
/*
Este comentario
tiene múltiples
líneas.
/* También puedes incluir comentarios,
dentro de comentarios multi línea.
*/
*/
Puedes combinar los comentarios de una sola línea con los de múltiples líneas para comentar/descomentar rápidamente secciones de código. Se llaman comentarios tijera por que pueden "cortar" un código para no ser ejecutado.
// /*
codigo()
// */
Al eliminar el comentario de la primera línea, el código será comentado. De esta forma rápidamente puedes activar o desactivar secciones de código.
/*
codigo()
// */
Puede aún ser más simplificado de esta forma
//*
codigo()
// */
Si se elimina el primer /
el código será comentado. Por lo que se ahorra un par de movimientos al realizar el comentario.
/*
codigo()
// */
En Wren no es posible llamar a los parámetros por su nombre. Por lo que si utilizas una función con algunos parámetros, puede ser útil comentarlos.
circulo(/* x */ 10, /* y */ 20, /* radio */ 10)
O mejor aún utilizar variables con nombres significativos
var x = 10
var y = 20
var radio = 10
circulo(x, y, radio)
-
Es completo, corto y directo. La mayoría de los comentarios deberían ser escritos en párrafos.
-
Explica tu forma de pensar, para que cuando regreses a leer el código en el futuro puedas comprender cómo se ha resuelto el problema.
-
También explica pensando en otros, para que otras personas puedan trabajar en tu código y entender cómo lo haz estructurado.
-
Explica una sección difícil con mayor detalle.
-
Cuando tienes que pensar cómo funciona el código antes de escribirlo.
-
Cuando probablemente olvides como estabas resolviendo un problema.
-
Cuando exista más de una forma de resolver un problema.
-
Cuando es poco probable que otros comprendan cómo haz resuelto un problema.
Escribir buenos comentarios es un indicador de un buen programador. Úsalos siempre. Verás comentarios a lo largo de los ejemplos en este documento.