una - Destrozando los mitos de Scala
peliculas de terror de estudiantes universitarios (13)
¿Cuáles son los conceptos erróneos más comunes sobre el lenguaje Scala y qué contraejemplos existen?
ACTUALIZAR
Estaba pensando más en varias afirmaciones que he visto, como "Scala está tipeado dinámicamente" y "Scala es un lenguaje de scripting".
Acepto que "Scala es [Simple / Complex]" podría considerarse un mito, pero también es un punto de vista muy dependiente del contexto. Mi creencia personal es que son las mismas características que pueden hacer que Scala aparezca, ya sea simple o compleja, dependiendo de quién las esté usando. En última instancia, el lenguaje simplemente ofrece abstracciones, y es la forma en que se utilizan las que dan forma a las percepciones.
No solo eso, sino que tiene una cierta tendencia a inflamar los argumentos, y todavía no he visto a nadie cambiar un punto de vista fuertemente sostenido sobre el tema ...
Algunos conceptos erróneos comunes relacionados con la biblioteca Actores:
- Los actores manejan los mensajes entrantes en paralelo, en múltiples hilos / contra un grupo de hilos (de hecho, manejar mensajes en múltiples hilos es contrario al concepto de actores y puede conducir a condiciones de carrera - todos los mensajes se manejan secuencialmente en un hilo (basado en hilo) los actores utilizan un hilo para el procesamiento y la ejecución de buzones, los actores basados en eventos pueden compartir un hilo de VM para su ejecución, utilizando el ejecutor de subprocesos múltiples para programar el procesamiento de buzones)
- Las excepciones no detectadas no cambian el comportamiento / estado del actor (de hecho, todas las excepciones no detectadas terminan al actor)
Creo que una idea errónea común entre muchos desarrolladores de scala, aquellos en EPFL (y usted mismo, Kevin) es que "Scala es un lenguaje simple" . El argumento suele ser algo como esto:
- scala tiene pocas palabras clave
- scala reutiliza las mismas pocas construcciones (por ejemplo, la sintaxis de PartialFunction se usa como el cuerpo de un bloque catch)
- Scala tiene algunas reglas simples que le permiten crear código de biblioteca (que puede aparecer como si el lenguaje tuviera palabras clave / construcciones especiales). Estoy pensando aquí de implicits; métodos que contienen dos puntos; símbolos de identificación permitidos; la equivalencia de
X(a, b)
ya X b
con extractores. Y así - La varianza del sitio de declaración de Scala significa que el sistema de tipo simplemente se sale de tu camino. No más comodines y
? super T
? super T
Mi opinión personal es que este argumento es completamente falso . El sistema de tipos de Scala junto con las implicidades le permite a uno escribir un código francamente impenetrable para el desarrollador promedio . Cualquier sugerencia de lo contrario es simplemente descabellada, independientemente de lo que las "métricas" anteriores puedan llevarlo a pensar. ( Nótese aquí que aquellos a quienes he visto burlarse de la no complejidad de Java en Twitter y en otros lugares resultan ser tipos súper inteligentes que, a veces, parecen tener mónadas, funcionadoras y flechas antes de que se quedaran sin recursos pantalones ).
Los argumentos obvios contra esto son (por supuesto):
- no tienes que escribir código como este
- no tiene que complacer al desarrollador promedio
De estos, me parece que solo el # 2 es válido. Ya sea que escriba o no un código tan complejo como scalaz , creo que es una tontería usar el lenguaje (y continuar scalaz ) sin una comprensión real del sistema de tipos. ¿De qué otra manera puede sacar lo mejor del lenguaje?
Existe el mito de que Scala es difícil porque Scala es un lenguaje complejo.
Esto es falso: por una variedad de métricas, Scala no es más complejo que Java. (Tamaño de la gramática, líneas de código o cantidad de clases o cantidad de métodos en la API estándar, etc.)
Pero es innegable que el código de Scala puede ser ferozmente difícil de entender. ¿Cómo puede ser esto, si Scala no es un lenguaje complejo?
La respuesta es que Scala es un lenguaje poderoso . A diferencia de Java, que tiene muchos constructos especiales (como enumeraciones) que logran una cosa en particular, y requiere aprender una sintaxis especializada que se aplica solo a eso, Scala tiene una variedad de construcciones muy generales. Al mezclar y combinar estos constructos, uno puede expresar ideas muy complejas con muy poco código. Y, como era de esperar, si alguien viene que no ha tenido la misma idea compleja y trata de descubrir qué es lo que está haciendo con este código tan compacto, puede resultarles desalentador, incluso más intimidante que si vieran una pareja. de páginas de código para hacer lo mismo, ¡ya que entonces al menos se darían cuenta de cuánto material conceptual había que entender!
También existe la cuestión de si las cosas son más complejas de lo que realmente deberían ser. Por ejemplo, algunas de las gimnasias de tipo presentes en la biblioteca de colecciones hacen que las colecciones sean un placer de usar pero desconcertantes de implementar o extender. Los objetivos aquí no son particularmente complicados (por ejemplo, las subclases deben devolver sus propios tipos), pero los métodos requeridos (tipos de alto grado, constructores implícitos, etc.) son complejos. (Tan complejo, de hecho, que Java simplemente se da por vencido y no lo intenta, en lugar de hacerlo "correctamente" como en Scala. Además, en principio, hay esperanza de que esto mejore en el futuro, ya que el método puede evolucionar para que coincida más estrechamente con el objetivo.) En otros casos, los objetivos son complejos; list.filter(_<5).sorted.grouped(10).flatMap(_.tail.headOption)
es un poco desordenado, pero si realmente quieres tomar todos los números menos de 5, y luego tomar cada 2do número de 10 en la lista restante, bueno, esa es solo una idea algo complicada, y el código prácticamente dice lo que hace si conoces las operaciones básicas de cobranza.
Resumen: Scala no es complejo, pero le permite expresar ideas complejas de forma compacta. La expresión compacta de ideas complejas puede ser desalentadora.
Existe el mito de que Scala no se puede implementar, mientras que una amplia gama de bibliotecas de Java de terceros se puede implementar sin pensarlo dos veces.
En la medida en que existe este mito, sospecho que existe entre personas que no están acostumbradas a separar una máquina virtual y API de un lenguaje y compilador. Si java == javac == API Java en tu mente, es posible que te pongas un poco nervioso si alguien sugiere usar scalac en lugar de javac, porque ves lo bien que funciona tu JVM.
Scala termina como código de bytes JVM, más su propia biblioteca personalizada. No hay motivo para preocuparse más por la implementación de Scala a pequeña escala o como parte de otro gran proyecto como lo es desplegar cualquier otra biblioteca que pueda o no ser compatible con la JVM que prefiera. De acuerdo, el equipo de desarrollo de Scala no está respaldado por tanta fuerza como las colecciones de Google, o Apache Commons, pero tiene al menos tanto peso detrás como el proyecto Java Advanced Imaging.
Mito:
def foo() = "something"
y
def bar = "something"
es el mismo.
No lo es; puede llamar a foo()
, pero bar()
intenta llamar al método apply de StringLike sin argumentos (resulta en un error).
Mito: La "opción" de Scala y los tipos "Quizás" de Haskell no te salvarán de nulo . :-)
Desenmascarado: ¿Por qué Scala "Option" y Haskell "Maybe" te salvarán de null por James Iry.
Mito: this.type
refiere al mismo tipo representado por this.getClass
.
Como un ejemplo de este concepto erróneo, uno podría suponer que en el siguiente código, el tipo de v.me
es B
:
trait A { val me: this.type = this }
class B extends A
val v = new B
En realidad, this.type
refiere al tipo cuya única instancia es this
. En general, x.type
es el tipo singleton cuya única instancia es x
. Entonces, en el ejemplo anterior, el tipo de v.me
es v.type
. La siguiente sesión demuestra el principio:
scala> val s = "a string"
s: java.lang.String = a string
scala> var v: s.type = s
v: s.type = a string
scala> v = "another string"
<console>:7: error: type mismatch;
found : java.lang.String("another string")
required: s.type
v = "another string"
Mito: Scala admite la sobrecarga del operador.
En realidad, Scala solo tiene reglas de nombres de método muy flexibles y sintaxis infija para la invocación de métodos, con reglas especiales para determinar la precedencia del método cuando la sintaxis infija se usa con ''operadores''. Esta distinción sutil tiene implicaciones críticas para la utilidad y el potencial de abuso de esta característica del lenguaje en comparación con la verdadera sobrecarga del operador (a la C ++), como se explica más a fondo en la respuesta de James Iry a esta pregunta .
Mito: Scala es altamente escalable, sin calificar qué formas de escalabilidad.
Scala puede ser altamente escalable en términos de la capacidad de expresar semántica denotacional de nivel superior, y esto lo convierte en un muy buen lenguaje para la experimentación e incluso para escalar la producción a nivel de proyecto de composicionalidad coordinada de arriba hacia abajo .
Sin embargo, cada lenguaje referencialmente opaco (es decir, que permita estructuras de datos mutables) es imperativo (y no declarativo) y no se escalará a la composición y seguridad de WAN ascendente, descoordinada . En otras palabras, los lenguajes imperativos son espaguetis compositivos (y de seguridad) y desarrollo descoordinado de módulos. Me doy cuenta de que tal desarrollo descoordinado quizás sea considerado actualmente por la mayoría como un "sueño imposible" y que tal vez no sea una prioridad. Y esto no es para desacreditar el beneficio de la composicionalidad (es decir, la eliminación de casos de esquina) que la unificación semántica de alto nivel puede proporcionar, por ejemplo, un modelo de teoría de categoría para la biblioteca estándar .
Posiblemente habrá disonancia cognitiva significativa para muchos lectores, y es probable que presionen prematuramente el voto "negativo", antes de comprender completamente el problema que planteo, especialmente dado que existen conceptos erróneos populares sobre lo imperativo y lo declarativo (es decir, mutable vs. inmutable) (y ansioso vs. perezoso ), por ejemplo, la semántica monádica nunca es intrínsecamente imperativa, pero hay una mentira que sí lo es . Sí, en Haskell, la mónada IO es imprescindible, pero no es nada importante ya que es una mónada.
Expliqué esto con más detalle en las secciones "Copute Tutorial" y "Pureza", que se encuentran en la página de inicio o temporalmente en este enlace .
Mi punto es que estoy muy agradecido de que Scala exista, pero quiero aclarar qué escala Scala y qué no. Necesito a Scala para lo que hace bien, es decir, para mí es la plataforma ideal para crear un nuevo lenguaje declarativo, pero Scala no es exclusivamente declarativo y el compilador Scala no puede aplicar una transparencia referencial afaik, aparte de recordar usar val
todas partes.
Creo que mi punto se aplica al debate sobre la complejidad de Scala. He descubierto (hasta ahora y principalmente conceptualmente, dado que hasta ahora tengo una experiencia limitada con mi nuevo lenguaje) que eliminar mutabilidad y bucles, al tiempo que conserva el subtipado de herencia múltiple de diamante (que Haskell no tiene), simplifica radicalmente el lenguaje. Por ejemplo, la ficción de la Unit
desaparece, y afaics, una serie de otros problemas y construcciones se vuelven innecesarios, por ejemplo, la biblioteca estándar de la teoría de no categoría, para las comprensiones, etc.
Mito: la coincidencia de patrones no encaja bien con el paradigma OO .
Destrozado here por el propio Martin Odersky. (También vea este documento - Objetos a juego con patrones - por Odersky et al.)
Mito: los métodos y las funciones son la misma cosa.
De hecho, una función es un valor (una instancia de una de las clases FunctionN
), mientras que un método no lo es. Jim McBeath explica las diferencias en mayor detalle . Las distinciones prácticas más importantes son:
- Solo los métodos pueden tener parámetros de tipo
- Solo los métodos pueden tomar argumentos implícitos
- Solo los métodos pueden tener parámetros nominales y predeterminados
- Cuando se hace referencia a un método, a menudo es necesario un guión bajo para distinguir la invocación de método de la aplicación de función parcial (por ejemplo,
str.length
evalúa a un número, mientras questr.length _
evalúa a una función de argumento cero).
Mito: puedes reemplazar un pliegue con un reducir al calcular algo así como una suma de cero.
Este es un error / concepto erróneo común entre los nuevos usuarios de Scala, particularmente aquellos que no tienen experiencia previa en programación funcional. Las siguientes expresiones no son equivalentes:
seq.foldLeft(0)(_+_)
seq.reduceLeft(_+_)
Las dos expresiones difieren en cómo manejan la secuencia vacía: el doblez produce un resultado válido (0), mientras que el reducir arroja una excepción.
No estoy de acuerdo con el argumento de que Scala es difícil porque puedes usar funciones muy avanzadas para hacer cosas difíciles con él. La escalabilidad de Scala significa que puede escribir abstracciones de DSL y API de alto nivel en Scala, que de otro modo necesitarían una extensión de idioma. Para ser justos, debe comparar las bibliotecas de Scala con otros compiladores de idiomas. La gente no dice que C # es difícil porque (supongo, no tengo conocimiento de primera mano sobre esto) el compilador de C # es bastante impenetrable. Para Scala, todo está al aire libre. Pero tenemos que llegar a un punto donde aclaremos que la mayoría de la gente no necesita escribir código en este nivel, ni deberían hacerlo.
Scala tiene tipos de inferencia y refinamiento de tipo (tipos estructurales), mientras que Java no.
El mito es busted por James Iry.