Uno de los aspectos más interesantes de diseñar un lenguaje de programación es descubrir que muchas de las características que damos por sentadas están profundamente relacionadas entre sí.
Un buen ejemplo es la representación de los valores nulos.
A simple vista parece una decisión menor. Sin embargo, termina afectando el sistema de tipos, el compilador, la biblioteca estándar e incluso el runtime del lenguaje.
En lugar de intentar inventar una solución completamente nueva, MateScript adopta una filosofía diferente: tomar buenas ideas de otros lenguajes y combinarlas de una forma consistente.
En este artículo aparecen varias influencias claras:
- El operador ? de Kotlin y C#.
- Los Union Types de TypeScript.
- Los objetos singleton de Scala.
Lo interesante es que ninguna de estas ideas fue incorporada de forma aislada. Todas terminan formando parte de un único diseño.
Desde el punto de vista del desarrollador, me gusta mucho la sintaxis introducida por Kotlin y posteriormente adoptada por C#.
let name: String?;
La intención es evidente.
La variable puede no contener un valor.
Sin embargo, no me gusta que el operador ? sea una característica mágica del compilador.
Siempre que sea posible prefiero que las características del lenguaje puedan explicarse utilizando otros conceptos ya existentes.
En lugar de convertir ? en una regla especial del compilador, podemos definirlo como un alias de tipos.
Es decir:
T?
es simplemente equivalente a:
Optional<T>
Por ejemplo:
let name: String?;
sería exactamente lo mismo que escribir:
let name: Optional<String>;
Una vez realizado este reemplazo, el compilador deja de conocer la existencia del operador ?.
Para el resto del proceso de compilación solamente existe Optional<T>.
Ahora aparece una pregunta mucho más interesante. ¿Cómo implementamos Optional<T>?
Podríamos imaginar algo como esto:
class Optional<T> {
private value: T;
}
Pero inmediatamente encontramos un problema. ¿Cómo representamos un value vacío?
En Java utilizaríamos null. Pero MateScript no tendrá un valor mágico llamado null.
Entonces necesitamos otra solución.
La idea tomada de Scala es tratar a Null como un objeto singleton del lenguaje.
Conceptualmente:
object Null
Esto significa que existe una única instancia de Null, exactamente igual que cualquier otro singleton.
No es una palabra reservada.
No es un valor especial incorporado por el compilador.
Es simplemente un objeto del lenguaje.
Hasta aquí parece que el problema está resuelto.
Pero todavía queda una pregunta.
Si value puede contener un objeto de tipo T o el objeto Null, entonces su tipo ya no puede ser simplemente T.
Necesitamos representar que un valor puede pertenecer a más de un tipo.
Es decir: T | Null
Y casi sin buscarlo llegamos a otro concepto conocido. Los Union Types.
La idea proviene de TypeScript.
Un Union Type representa un valor que puede pertenecer a uno de varios tipos.
Por ejemplo:
String | Null
indica que un valor puede ser:
un objeto String, o
el objeto Null.
Gracias a esto podemos redefinir Optional.
class Optional<T> {
private value: T | Null;
}
Ahora sí el modelo es completamente consistente.
Un Optional<String> puede contener un String o el objeto Null.
No existe ningún valor mágico dentro del lenguaje.
Todo está expresado mediante el sistema de tipos.
Lo interesante es que los Union Types no aparecieron porque quisiera agregarlos al lenguaje.
Surgieron como una consecuencia natural del diseño de Optional.
Una vez que el compilador fue capaz de representar tipos como: T | Null apareció una pregunta inevitable.
¿Por qué limitar esa capacidad únicamente a Optional?
Si el compilador ya sabe trabajar con Union Types, también puede permitir que los desarrolladores los utilicen directamente.
Por ejemplo:
let value: String | Int;
let result: Success | Error;
let animal: Dog | Cat;
Lo que comenzó siendo una solución para representar la ausencia de un valor terminó convirtiéndose en una nueva característica del lenguaje.
Curiosamente, cada una de estas decisiones resuelve un problema distinto, pero juntas forman un diseño mucho más consistente.
El desarrollador escribe:
String?
El parser lo transforma en:
Optional<String>
Y Optional se implementa como:
Optional<T>
└── value : T | Null
Todo utilizando conceptos generales del lenguaje.
No existen excepciones.
No existen reglas especiales.
No existen valores mágicos.
Diseñar un lenguaje muchas veces consiste en descubrir que una buena solución termina resolviendo varios problemas al mismo tiempo.
En MateScript decidimos combinar ideas provenientes de distintos lenguajes:
- De Kotlin y C# tomamos la sintaxis ? para representar tipos opcionales.
- De TypeScript adoptamos los Union Types para representar valores que pueden pertenecer a más de un tipo.
- De Scala incorporamos la idea de los objetos singleton para representar Null como un objeto del lenguaje y no como un valor mágico del compilador.
Lo interesante es que ninguna de estas características fue agregada por separado.
Cada decisión fue llevando naturalmente a la siguiente, hasta formar un sistema de tipos más uniforme y coherente.

No hay comentarios.:
Publicar un comentario