No tengo un doctorado, ni ningún otro tipo de título, ni en CS ni en matemáticas, ni en ningún otro campo. No tengo experiencia previa con Scala ni ningún otro lenguaje similar. No tengo experiencia incluso con sistemas de tipo remotamente comparables. De hecho, el único lenguaje que tengo más que un conocimiento superficial del cual incluso tiene un sistema de tipos es Pascal, no exactamente conocido por su sofisticado sistema de tipos. (A pesar de que hace tener tipos de rango, que yo sepa prácticamente ningún otro idioma tiene, pero eso no es realmente relevante aquí.) Los otros tres idiomas que conozco son básicas, Smalltalk y Ruby, ninguno de los cuales ni siquiera un sistema de tipos.
Y, sin embargo, no tengo ningún problema para comprender la firma de la map
función que publicaste. Me parece más o menos la misma firma que map
tiene en todos los demás idiomas que he visto. La diferencia es que esta versión es más genérica. Parece más una cosa C ++ STL que, por ejemplo, Haskell. En particular, se abstrae del tipo de colección concreta al solo requerir que el argumento sea IterableLike
, y también se abstrae del tipo de retorno concreto al solo requerir que exista una función de conversión implícita que pueda construir algo a partir de esa colección de valores de resultados. Sí, eso es bastante complejo, pero realmente es solo una expresión del paradigma general de la programación genérica: no asumas nada que realmente no tengas que hacer.
En este caso, en map
realidad no necesita que la colección sea una lista, que se ordene o que se pueda ordenar o algo así. Lo único que map
le importa es que puede obtener acceso a todos los elementos de la colección, uno tras otro, pero sin ningún orden en particular. Y no necesita saber cuál es la colección resultante, solo necesita saber cómo construirla. Entonces, eso es lo que requiere su firma de tipo.
Entonces, en lugar de
map :: (a → b) → [a] → [b]
cuál es la firma de tipo tradicional map
, se generaliza para no requerir una estructura de datos concreta List
sino más bienIterableLike
map :: (IterableLike i, IterableLike j) ⇒ (a → b) → i → j
que luego se generaliza al requerir solo que exista una función que pueda convertir el resultado a cualquier estructura de datos que el usuario desee:
map :: IterableLike i ⇒ (a → b) → i → ([b] → c) → c
Admito que la sintaxis es un poco más complicada, pero la semántica es la misma. Básicamente, comienza desde
def map[B](f: (A) ⇒ B): List[B]
que es la firma tradicional para map
. (Observe cómo, debido a la naturaleza orientada a objetos de Scala, el parámetro de la lista de entrada desaparece, porque ahora es el parámetro receptor implícito que tiene cada método en un sistema OO de despacho único). Luego se generalizó de un concreto List
a uno más generalIterableLike
def map[B](f: (A) ⇒ B): IterableLike[B]
Ahora, reemplaza la IterableLike
colección de resultados con una función que produce , bueno, realmente casi cualquier cosa.
def map[B, That](f: A ⇒ B)(implicit bf: CanBuildFrom[Repr, B, That]): That
Que realmente no creo es que difícil de entender. Realmente solo necesitas un par de herramientas intelectuales:
- Necesita saber (aproximadamente) qué
map
es. Si admitió solo la firma de tipo sin el nombre del método, lo admito, sería mucho más difícil averiguar qué está sucediendo. Pero como ya sabe lo que map
se supone que debe hacer, y sabe cuál es su firma de tipo, puede escanear rápidamente la firma y enfocarse en las anomalías, como "¿por qué esto map
toma dos funciones como argumentos, no una?"
- Debe poder leer realmente la firma de tipo. Pero incluso si nunca antes ha visto Scala, esto debería ser bastante fácil, ya que en realidad es solo una mezcla de sintaxis de tipos que ya conoce de otros idiomas: VB.NET usa corchetes para el polimorfismo paramétrico, y usa una flecha para denotar el tipo de retorno y dos puntos para separar el nombre y el tipo, es en realidad la norma.
- Necesita saber aproximadamente de qué se trata la programación genérica. (Que no es que difícil de entender, ya que es básicamente todo lo explicado en el nombre: está literalmente a la programación de un modo genérico).
Ninguno de estos tres debería causar dolor de cabeza a ningún programador profesional o incluso aficionado. map
ha sido una función estándar en casi todos los idiomas diseñados en los últimos 50 años, el hecho de que diferentes idiomas tengan una sintaxis diferente debería ser obvio para cualquiera que haya diseñado un sitio web con HTML y CSS y no puede suscribirse a una programación remota lista de correo relacionada sin algunos fanáticos molestos de C ++ de la iglesia de St. Stepanov explicando las virtudes de la programación genérica.
Sí, Scala es complejo. Sí, Scala tiene uno de los sistemas de tipos más sofisticados conocidos por el hombre, rivalizando e incluso superando lenguajes como Haskell, Miranda, Clean o Cyclone. Pero si la complejidad fuera un argumento contra el éxito de un lenguaje de programación, C ++ habría muerto hace mucho tiempo y todos estaríamos escribiendo Scheme. Hay muchas razones por las cuales Scala probablemente no tendrá éxito, pero el hecho de que los programadores no se molesten en encender sus cerebros antes de sentarse frente al teclado probablemente no sea el principal.