scala scala-2.8 scala-collections

¿Es la biblioteca de colecciones Scala 2.8 un caso de "la nota de suicidio más larga de la historia"?



scala-2.8 scala-collections (18)

Acabo de comenzar a ver la reimplementación de la biblioteca de colecciones de Scala que se publicará en la inminente versión 2.8 . Aquellos que estén familiarizados con la biblioteca de 2.7 notarán que la biblioteca, desde una perspectiva de uso, ha cambiado poco. Por ejemplo...

> List("Paris", "London").map(_.length) res0: List[Int] List(5, 6)

... funcionaría en cualquiera de las dos versiones. La biblioteca es eminentemente útil : de hecho, es fantástica. Sin embargo, aquellos que antes no estaban familiarizados con Scala y buscaban el idioma ahora tienen que entender las firmas de los métodos como:

def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That

Para una funcionalidad tan simple, esta es una firma desalentadora y una que me cuesta entender. No creo que Scala pudiera ser el próximo Java (o / C / C ++ / C #). No creo que sus creadores lo apuntaran a ese mercado, pero creo que es / es ciertamente factible que Scala se convierta en el próximo Ruby o Python (es decir, para obtener una importante base de usuarios comerciales)

  • ¿Esto va a hacer que la gente venga a Scala?
  • ¿Esto le dará a Scala un mal nombre en el mundo comercial como un juego académico que solo los estudiantes de doctorado pueden entender? ¿Los CTO y los jefes de software se van a asustar?
  • ¿Fue la biblioteca rediseñada una idea sensata?
  • Si estás usando Scala comercialmente, ¿estás preocupado por esto? ¿Planeas adoptar 2.8 inmediatamente o esperar a ver qué pasa?

Steve Yegge una vez atacó a Scala (erróneamente en mi opinión) por lo que vio como su sistema de tipos demasiado complicado. Me preocupa que alguien vaya a tener un día de campo para difundir FUD con esta API (de manera similar a como Josh Bloch asustó al JCP al agregar cierres a Java).

Nota : debo dejar claro que, si bien creo que Joshua Bloch fue influyente en el rechazo de la propuesta de cierres de BGGA, no atribuyo esto a nada que no sea su creencia honesta de que la propuesta representó un error.

A pesar de lo que me digan mi esposa y compañeros de trabajo, no creo que sea idiota: tengo un buen título en matemáticas de la Universidad de Oxford , y he programado comercialmente durante casi 12 años y en Scala durante aproximadamente 12 años. Un año (también comercialmente).

Tenga en cuenta que el título del tema es una cita hecha sobre el manifiesto de un partido político del Reino Unido a principios de los años ochenta . Esta pregunta es subjetiva, pero es una pregunta genuina, la hice CW y me gustaría tener algunas opiniones sobre el tema.


¿Esto va a hacer que la gente venga a Scala?

Sí, pero también evitará que la gente se desanime. He considerado que la falta de colecciones que utilizan tipos de tipo superior es una debilidad importante desde que Scala obtuvo apoyo para los tipos de tipo superior. Hace que los documentos API sean más complicados, pero realmente hace que el uso sea más natural.

¿Esto va a dar a Scala un mal nombre en el mundo comercial como un juego académico que solo los estudiantes de doctorado pueden entender? ¿Los CTO y los jefes de software se van a asustar?

Algunos probablemente lo harán. No creo que Scala sea accesible para muchos desarrolladores "profesionales", en parte debido a la complejidad de Scala y en parte debido a la falta de voluntad de muchos desarrolladores para aprender. Los CTOs que emplean a tales desarrolladores se asustarán con razón.

¿Fue la biblioteca rediseñada una idea sensata?

Absolutamente. Hace que las colecciones encajen mucho mejor con el resto del idioma y el sistema de tipos, incluso si todavía tiene algunos bordes ásperos.

Si estás usando scala comercialmente, ¿estás preocupado por esto? ¿Planeas adoptar 2.8 inmediatamente o esperar a ver qué pasa?

No lo estoy usando comercialmente. Probablemente esperaré al menos un par de revoluciones en la serie 2.8.x antes de intentar introducirla para que los insectos puedan ser eliminados. También esperaré a ver cuánto éxito tiene EPFL para mejorar su desarrollo y procesos de lanzamiento. Lo que veo parece esperanzador, pero trabajo para una compañía conservadora.

Uno de los temas más generales de "¿es Scala demasiado complicado para los desarrolladores principales?" ...

La mayoría de los desarrolladores, principales o no, mantienen o amplían los sistemas existentes. Esto significa que la mayoría de lo que usan está dictado por decisiones tomadas hace mucho tiempo. Todavía hay mucha gente escribiendo COBOL.

El desarrollador principal de mañana trabajará manteniendo y extendiendo las aplicaciones que se están construyendo hoy. Muchas de estas aplicaciones no están siendo construidas por desarrolladores convencionales. Los desarrolladores principales de mañana utilizarán el lenguaje que están utilizando los desarrolladores de aplicaciones más exitosos de la actualidad.


¿Esto va a hacer que la gente venga a Scala?

No creo que sea el factor principal que afectará la popularidad de Scala, ya que Scala tiene mucha potencia y su sintaxis no es tan extraña para un programador de Java / C ++ / PHP como Haskell, OCaml, SML, Lisps, etc.

Pero sí creo que la popularidad de Scala se estabilizará en menos de lo que es Java hoy en día, porque también creo que el siguiente lenguaje general debe ser mucho más simplificado, y la única manera de verlo es la inmutabilidad pura, es decir, declarativa como HTML, pero Turing está completa . Sin embargo, estoy parcializado porque estoy desarrollando un lenguaje así, pero solo lo hice después de descartar un estudio de varios meses en el que Scala no podía ser suficiente para lo que necesitaba.

¿Esto le dará a Scala un mal nombre en el mundo comercial como un juego académico que solo los estudiantes de doctorado pueden entender? ¿Los CTO y los jefes de software se van a asustar?

No creo que la reputación de Scala sufra por el complejo de Haskell. Pero creo que algunos retrasarán el aprendizaje, porque para la mayoría de los programadores, todavía no veo un caso de uso que los obligue a usar Scala, y postergarán su aprendizaje. Quizás el lado del servidor altamente escalable sea el caso de uso más convincente.

Y, para el mercado general, el primer aprendizaje de Scala no es un "soplo de aire fresco", donde uno está escribiendo programas de forma inmediata, como el primer uso de HTML o Python. Scala tiende a crecer en ti, después de que uno aprende todos los detalles con los que tropieza desde el principio. Sin embargo, tal vez si hubiera leído Programación en Scala desde el principio, mi experiencia y opinión sobre la curva de aprendizaje habrían sido diferentes.

¿Fue la biblioteca rediseñada una idea sensata?

Seguro.

Si estás usando Scala comercialmente, ¿estás preocupado por esto? ¿Planeas adoptar 2.8 inmediatamente o esperar a ver qué pasa?

Estoy usando Scala como la plataforma inicial de mi nuevo idioma. Probablemente no estaría creando código en la biblioteca de la colección de Scala si estuviera usando Scala comercialmente de otra manera. Yo crearía mi propia biblioteca basada en la teoría de categorías, ya que la única vez que miré, encontré las firmas de tipo de Scalaz aún más detalladas y complejas que la biblioteca de colecciones de Scalaz. Quizás parte de ese problema sea la forma en que Scala implementa clases de tipo, y esa es una razón menor por la que estoy creando mi propio idioma.

Decidí escribir esta respuesta, porque quería forzarme a investigar y comparar el diseño de la clase de colección de Scala con el que estoy haciendo para mi idioma. También podría compartir mi proceso de pensamiento.

El uso de 2.8 colecciones de Scala de una abstracción de constructor es un principio de diseño de sonido. Quiero explorar dos compromisos de diseño a continuación.

  1. CÓDIGO ÚNICAMENTE POR ESCRITO : Después de escribir esta sección, leí el comentario de Carl Smotricz que concuerda con lo que espero que sea la compensación. Los comentarios de James Strachan y davetron5000 coinciden en que el significado de Eso (ni siquiera es Eso [B]) y el mecanismo de lo implícito no es fácil de comprender intuitivamente. Vea mi uso de monoide en el número 2 a continuación, que creo que es mucho más explícito. El comentario de Derek Mahar es sobre escribir Scala, pero sobre leer Scala de otros que no es "en los casos comunes".

    Una crítica que he leído sobre Scala es que es más fácil escribirlo que leer el código que otros han escrito. Y encuentro que esto es cierto ocasionalmente por varias razones (por ejemplo, muchas formas de escribir una función, cierres automáticos, Unidad para DSL, etc.), pero no estoy decidido si este es un factor importante. Aquí el uso de parámetros de función implícitos tiene ventajas y desventajas. En el lado positivo, reduce la verbosidad y automatiza la selección del objeto generador. En el example de Odersky.examplela conversión de un BitSet, es decir, Set [Int], a un Set [String] es implícita. El lector desconocido del código podría no saber fácilmente cuál es el tipo de colección, a menos que puedan razonar bien sobre todos los posibles candidatos al constructor implícito invisible que puedan existir en el alcance del paquete actual. Por supuesto, el programador experimentado y el escritor del código sabrán que BitSet está limitado a Int, por lo tanto, un mapa a String debe convertirse a un tipo de colección diferente. ¿Pero qué tipo de colección? No se especifica explícitamente.

  2. DISEÑO DE LA COLECCIÓN AD-HOC: Después de escribir esta sección, leí el comentario de Tony Morris y me di cuenta de que estaba haciendo casi el mismo punto. Quizás mi exposición más detallada aclare el punto.

    En "Fighting Bit Rot with Types" Odersky & Moors, se presentan dos casos de uso. Son la restricción de los elementos BitSet a Int, y Map to pair tuple, y se proporcionan como la razón por la cual la función de mapeo de elementos general, A => B, debe ser capaz de crear tipos de colección de destino alternativos. Sin embargo, afaik esto es defectuoso desde la perspectiva de la teoría de categorías. Para ser consistentes en la teoría de categorías y así evitar los casos de esquina, estos tipos de colección son funtores, en los cuales cada morfismo, A => B, debe mapearse entre objetos en la misma categoría de functor, Lista [A] => Lista [B], BitSet [A] => BitSet [B]. Por ejemplo, una Opción es un functor que se puede ver como una colección de conjuntos de un solo (objeto) y ninguno. No hay un mapa general desde Option''s None, o List''s Nil, a otros functors que no lo hacent tiene un estado "vacío".

    Hay una opción de diseño de compensación hecha aquí. En el diseño para la biblioteca de colecciones de mi nuevo idioma, elegí hacer de todo un funtor, lo que significa que si implemento un BitSet, debe ser compatible con todos los tipos de elementos, mediante el uso de una representación interna de campo de bit cuando se presenta con un no parámetro de tipo entero, y esa funcionalidad ya está en el Conjunto del cual se hereda en Scala. Y Map en mi diseño necesita mapear solo sus valores, y puede proporcionar un método no funtor separado para mapear sus tuplas de par (clave, valor). Una ventaja es que cada funtor es generalmente también un aplicativo y quizás una mónada también. Por lo tanto, todas las funciones entre los tipos de elementos, por ejemplo, A => B => C => D => ..., se elevan automáticamente a las funciones entre los tipos de aplicativos elevados, por ejemplo, Lista [A] => Lista [B] => Lista [ C] => Lista [D] => ....Para la asignación de un funtor a otra clase de colección, ofrezco una sobrecarga de mapa que toma un monoide, por ejemplo, Nil, None, 0, "", Array (), etc. Así que la función de abstracción del constructor es el método de adición de un monoid y se suministra explícitamente como un parámetro de entrada necesario, por lo tanto, sin conversiones implícitas invisibles. (Tangente: este parámetro de entrada también permite agregar monoides no vacíos, lo que el diseño del mapa de Scala no puede hacer). Tales conversiones son un mapa y un pliegue en la misma pasada de iteración. También proporciono un McBride & Patterson "Programación aplicativa con efectos" transitable, en el sentido de la categoría, que también permite mapear y plegar en un solo pase de iteración desde cualquier aplicable a cualquier aplicativo, donde la mayoría de las clases de colección son ambas.Además, la mónada estatal es un aplicativo y, por lo tanto, es una abstracción del constructor totalmente generalizada de cualquier posibilidad de desplazamiento.

    Entonces, afaicas, las colecciones de Scala son "ad hoc" en el sentido de que no están basadas en la teoría de categorías, y la teoría de categorías es la esencia de la semántica denotacional de nivel superior. Si bien los constructores implícitos de Scala son, a primera vista, "más generalizados" que un modelo de funtor + constructor monoide + atravesable -> aplicativo, no se ha demostrado que afaik sea compatible con ninguna categoría, por lo que no sabemos qué reglas siguen El sentido más general y los casos de esquina que se darán pueden no obedecer a ningún modelo de categoría. Simplemente no es cierto que agregar más variables haga algo más general, y este fue uno de los grandes beneficios de la teoría de categorías, ya que proporciona reglas mediante las cuales mantener la generalidad mientras se eleva a la semántica de nivel superior. Una colección es una categoría.

    Leí en alguna parte, creo que fue Odersky, como otra justificación para el diseño de la biblioteca, es que la programación en un estilo puramente funcional tiene el costo de la recursión limitada y la velocidad donde no se usa la recursión de la cola. No me ha resultado difícil emplear la recursión de cola en todos los casos que he encontrado hasta ahora.

Además, tengo en mente una idea incompleta de que algunas de las concesiones de Scala se deben a intentar ser un lenguaje mutable e inmutable, a diferencia de, por ejemplo, Haskell o el lenguaje que estoy desarrollando. Esto concuerda con el comentario de Tony Morris acerca de las comprensiones. En mi idioma, no hay bucles ni construcciones mutables. Mi idioma se sentará en la parte superior de Scala (por ahora) y le debe mucho, y esto no sería posible si Scala no tuviera el sistema de tipo general y la mutabilidad. Sin embargo, eso podría no ser cierto, porque creo que Odersky y Moors ("Fighting Bit Rot with Types") son incorrectos al afirmar que Scala es el único lenguaje OOP con tipos más altos, porque verifiqué (a mí mismo y a través de Bob Harper) que Standard ML los tiene. También parece que el sistema de tipos de SML puede ser equivalente flexible (desde la década de 1980),lo que puede no apreciarse fácilmente porque la sintaxis no es tan similar a Java (y C ++ / PHP) como a Scala. En cualquier caso, esto no es una crítica de Scala, sino más bien un intento de presentar un análisis incompleto de las compensaciones, que espero que sea pertinente a la pregunta. Scala y SML no sufren la incapacidad de Haskell para hacerloLa herencia múltiple de diamante , que es crítica y entiendo, es por qué tantas funciones en el Preludio de Haskell se repiten para diferentes tipos.


Bueno, puedo entender su dolor, pero, francamente, las personas como usted y yo, o casi cualquier usuario habitual de , no son la regla.

Lo que quiero decir con eso es que ... ¡a la mayoría de los programadores no les importará ese tipo de firma, porque nunca los verán ! No leen documentación.

Mientras vean algún ejemplo de cómo funciona el código, y el código no les falla al producir el resultado que esperan , nunca mirarán la documentación. Cuando eso falla, mirarán la documentación y esperarán ver ejemplos de uso en la parte superior.

Con estas cosas en mente, pienso que:

  1. Cualquier persona (como en la mayoría de las personas) que se haya topado con ese tipo de firma se burlará de Scala hasta el final si está predestinada, y lo considerará como un símbolo del poder de Scala si le gusta a Scala.

  2. Si la documentación no está mejorada para proporcionar ejemplos de uso y explicar claramente para qué es un método y cómo usarlo, puede restar mérito a la adopción de Scala.

  3. A la larga, no importará. El hecho de que Scala pueda hacer cosas así hará que las bibliotecas escritas para Scala sean mucho más potentes y seguras de usar. Estas bibliotecas y marcos atraerán a programadores atraídos por herramientas poderosas.

  4. Los programadores a quienes les gusta la simplicidad y la franqueza continuarán usando PHP o lenguajes similares.

Lamentablemente, los programadores de Java están muy interesados ​​en las herramientas de poder, así que, al responder eso, acabo de revisar mi expectativa de la adopción de Scala en general. No tengo ninguna duda de que Scala se convertirá en un lenguaje común. No C-mainstream, pero quizás Perl-mainstream o PHP-mainstream.

Hablando de Java, ¿alguna vez reemplazaste el cargador de clases? ¿Alguna vez has investigado lo que eso implica? Java puede dar miedo, si miras los lugares que hacen los escritores de marcos. Es solo que la mayoría de la gente no lo hace. Lo mismo se aplica a Scala, IMHO, pero los primeros adoptadores tienen la tendencia de mirar debajo de cada roca que encuentran, para ver si hay algo escondido allí.


Creo que el principal problema con ese método es que (implicit bf : CanBuildFrom[Repr, B, That]) no tiene explicación. Aunque sé qué argumentos implícitos son, no hay nada que indique cómo afecta esto a la llamada. Perseguir a través del scaladoc solo me deja más confundido (algunas de las clases relacionadas con CanBuildFrom incluso tienen documentación).

Creo que un simple "debe haber un objeto implícito en el alcance de bf que proporcione un generador para objetos de tipo B en el tipo de retorno That " ayudaría un poco, pero es un concepto llamativo cuando todo lo que realmente quieres hacer es mapear A a B De hecho, no estoy seguro de que sea correcto, porque no sé lo que significa el tipo Repr , y la documentación de Traversable ciertamente no da ninguna pista.

Entonces, me quedan dos opciones, ninguna de las cuales es agradable:

  • Supongamos que solo funcionará cómo funciona el mapa antiguo y cómo funciona el mapa en la mayoría de los otros idiomas
  • Cavar en el código fuente un poco más

Entiendo que Scala esencialmente está exponiendo las entrañas de cómo funcionan estas cosas y que, en última instancia, es una forma de hacer lo que describe oxbow_lakes. Pero es una distracción en la firma.


Desafortunadamente, la firma para el mapa que dio es incorrecta para el mapa y, de hecho, existe una crítica legítima.

La primera crítica es que al subvertir la firma del mapa, tenemos algo que es más general. Es un error común creer que esta es una virtud por defecto. No lo es La función de mapa está muy bien definida como un funtor covariante Fx -> (x -> y) -> Fy con adherencia a las dos leyes de composición e identidad. Cualquier otra cosa atribuida al "mapa" es una parodia.

La firma dada es otra cosa, pero no es un mapa. Lo que sospecho que está intentando ser es una versión especializada y ligeramente alterada de la firma "transversal" del artículo, La esencia del patrón de iterador. Aquí está su firma:

traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)

Lo convertiré a Scala:

def traverse[A, B](f: A => F[B], a: T[A])(implicit t: Traversable[T], ap: Applicative[F]): F[T[B]

Por supuesto que falla, ¡no es lo suficientemente general! Además, es ligeramente diferente (tenga en cuenta que puede obtener el mapa ejecutando el recorrido a través del functor de identidad). Sin embargo, sospecho que si los escritores de la biblioteca estuvieran más al tanto de las generalizaciones de la biblioteca que están bien documentadas (la Programación aplicativa con efectos precede a lo mencionado anteriormente), entonces no veríamos este error.

Segundo, la función del mapa es un caso especial en Scala debido a su uso en comprensiones. Desafortunadamente, esto significa que un diseñador de bibliotecas que está mejor equipado no puede ignorar este error sin sacrificar el azúcar sintáctico de las comprensiones. En otras palabras, si los diseñadores de la biblioteca de Scala destruyeran un método, entonces esto se ignora fácilmente, ¡pero por favor no mapee!

Espero que alguien lo diga, porque tal como es, será más difícil solucionar los errores que Scala insiste en cometer, aparentemente por razones a las que tengo fuertes objeciones. Es decir, la solución a "las objeciones irresponsables del programador promedio (es decir, ¡demasiado difícil!)" No es "apaciguarlas para que sea más fácil para ellos", sino proporcionar punteros y asistencia para convertirse en mejores programadores. Los objetivos de Scala y yo estamos en disputa sobre este tema, pero volvemos a su punto.

Probablemente estaba haciendo su punto, prediciendo respuestas específicas del "programador promedio". Es decir, las personas que reclamarán "¡pero es demasiado complicado!" o algo así. Estos son los Yegges o Blochs a los que te refieres. Mi respuesta a estas personas del movimiento antiintelectualismo / pragmatismo es bastante dura y ya estoy anticipando un aluvión de respuestas, así que lo omitiré.

Realmente espero que las bibliotecas de Scala mejoren, o al menos, que los errores puedan ser guardados en una esquina. Java es un lenguaje en el que "tratar de hacer algo útil" es tan increíblemente costoso, que a menudo no vale la pena porque la abrumadora cantidad de errores simplemente no se puede evitar. Imploro a Scala que no siga el mismo camino.


Espero que no sea una "nota de suicidio", pero puedo ver tu punto. Usted golpea en lo que es al mismo tiempo tanto una fortaleza como un problema de Scala: su extensibilidad . Esto nos permite implementar la funcionalidad más importante en las bibliotecas. En algunos otros idiomas, las secuencias con algo como map o collect se construirían, y nadie tiene que ver todos los aros por los que debe pasar el compilador para que funcionen sin problemas. En Scala, todo está en una biblioteca, y por lo tanto, a la intemperie.

De hecho, la funcionalidad del map que es compatible con su tipo complicado es bastante avanzada. Considera esto:

scala> import collection.immutable.BitSet import collection.immutable.BitSet scala> val bits = BitSet(1, 2, 3) bits: scala.collection.immutable.BitSet = BitSet(1, 2, 3) scala> val shifted = bits map { _ + 1 } shifted: scala.collection.immutable.BitSet = BitSet(2, 3, 4) scala> val displayed = bits map { _.toString + "!" } displayed: scala.collection.immutable.Set[java.lang.String] = Set(1!, 2!, 3!)

¿Ves cómo siempre obtienes el mejor tipo posible? Si BitSet Int s a Int s, vuelve a obtener un BitSet , pero si BitSet Int s a String s, obtiene un Set general. Tanto el tipo estático como la representación en tiempo de ejecución del resultado del mapa dependen del tipo de resultado de la función que se le pasa. Y esto funciona incluso si el conjunto está vacío, por lo que la función nunca se aplica. Que yo sepa, no hay otro marco de recopilación con una funcionalidad equivalente. Sin embargo, desde la perspectiva del usuario, así es como se supone que funcionan las cosas.

El problema que tenemos es que toda la tecnología inteligente que hace que esto ocurra se filtra hacia las firmas de tipo que se vuelven grandes y aterradoras. ¿Pero tal vez un usuario no debería mostrar de forma predeterminada la firma de tipo completo del map ? ¿Qué tal si ella buscó un map en BitSet ?

map(f: Int => Int): BitSet (click here for more general type)

Los documentos no estarían en ese caso, porque desde la perspectiva del usuario, el mapa tiene el tipo (Int => Int) => BitSet . Pero el map también tiene un tipo más general que se puede inspeccionar haciendo clic en otro enlace.

Todavía no hemos implementado una funcionalidad como esta en nuestras herramientas. Pero creo que tenemos que hacer esto, para evitar asustar a la gente y dar información más útil. Con herramientas como esa, es de esperar que los marcos inteligentes y las bibliotecas no se conviertan en notas suicidas.


Lo mismo en C ++:

template <template <class, class> class C, class T, class A, class T_return, class T_arg > C<T_return, typename A::rebind<T_return>::other> map(C<T, A> &c,T_return(*func)(T_arg) ) { C<T_return, typename A::rebind<T_return>::other> res; for ( C<T,A>::iterator it=c.begin() ; it != c.end(); it++ ){ res.push_back(func(*it)); } return res; }


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 idioma similar. No tengo experiencia con sistemas de tipos remotamente comparables. De hecho, el único lenguaje del que tengo más que un conocimiento superficial del cual incluso tiene un sistema de tipo es Pascal, no conocido exactamente por su sistema de tipo sofisticado. (Aunque tiene tipos de rango, AFAIK casi ningún otro idioma tiene, pero eso no es realmente relevante aquí). Los otros tres idiomas que conozco son BASIC, Smalltalk y Ruby, ninguno de los cuales tiene un sistema de tipos.

Y, sin embargo, no tengo ningún problema en comprender la firma de la función de map que ha publicado. Me parece que es casi la misma firma que tiene el map en todos los otros idiomas que he visto. La diferencia es que esta versión es más genérica. Parece más una cosa de C ++ STL que, por ejemplo, Haskell. En particular, se abstrae del tipo de colección concreto al requerir solo 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 en realidad es solo una expresión del paradigma general de la programación genérica: no asuma nada que realmente no tenga que hacer.

En este caso, el map no necesita realmente que la colección sea una lista, o que se ordene o se pueda ordenar o algo así. Lo único que le importa al map 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 qué es la colección resultante, solo necesita saber cómo construirla. Entonces, eso es lo que requiere su tipo de firma.

Entonces, en lugar de

map :: (a → b) → [a] → [b]

que es la firma de tipo tradicional para el map , está generalizada para no requerir una List concreta sino una estructura de datos IterableLike

map :: (IterableLike i, IterableLike j) ⇒ (a → b) → i → j

que luego se generaliza al requerir que exista una función que pueda convertir el resultado a la 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 torpe, pero la semántica es la misma. Básicamente, se inicia desde

def map[B](f: (A) ⇒ B): List[B]

que es la firma tradicional para el map . (Observe cómo debido a la naturaleza orientada a objetos de Scala, el parámetro de 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 una List concreta a una más. IterableLike general

def map[B](f: (A) ⇒ B): IterableLike[B]

Ahora, reemplaza la colección de resultados IterableLike con una función que produce , bueno, en realidad casi cualquier cosa.

def map[B, That](f: A ⇒ B)(implicit bf: CanBuildFrom[Repr, B, That]): That

Lo que realmente creo no es tan difícil de entender. Solo hay un par de herramientas intelectuales que necesitas:

  1. Necesitas saber (aproximadamente) qué map es. Si solo dio la firma de tipo sin el nombre del método, lo admito, sería mucho más difícil averiguar qué está pasando. Pero como ya sabe qué se supone que debe hacer el map , y sabe qué tipo de firma se supone que debe ser, puede escanear rápidamente la firma y concentrarse en las anomalías, como "¿por qué este map toma dos funciones como argumentos, no una ? "
  2. Necesitas poder leer el tipo de firma. Pero incluso si nunca antes ha visto Scala, esto debería ser bastante fácil, ya que realmente es solo una mezcla de sintaxis de tipo que ya conoce en otros idiomas: VB.NET usa corchetes para el polimorfismo paramétrico, y usa una flecha para indicar el tipo de retorno y dos puntos para separar nombre y tipo, es en realidad la norma.
  3. Necesitas saber aproximadamente de qué se trata la programación genérica. (Lo que no es tan difícil de descifrar, ya que básicamente todo está deletreado en el nombre: literalmente es solo una programación genérica).

Ninguno de estos tres debería dar a un programador profesional o incluso aficionado un dolor de cabeza serio. 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 los 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 de forma remota. programación relacionada con la lista de correo sin algún molesto fanático de C ++ de la iglesia de St. Stepanov que explica las virtudes de la programación genérica.

Sí, Scala es complejo. Sí, Scala tiene uno de los sistemas tipográficos 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 en contra del é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 que Scala probablemente no tendrá éxito, pero el hecho de que a los programadores no les moleste encender sus cerebros antes de sentarse frente al teclado probablemente no sea la principal.


Soy un principiante de Scala y honestamente no veo ningún problema con esa firma de tipo. El parámetro es la función para asignar y el parámetro implícito el constructor para devolver la colección correcta. Claro y legible.

Todo es bastante elegante, en realidad. Los parámetros de tipo generador permiten que el compilador elija el tipo de retorno correcto, mientras que el mecanismo de parámetros implícitos oculta este parámetro adicional del usuario de la clase. Intenté esto:

Map(1 -> "a", 2 -> "b").map((t) => (t._2) -> (t._1)) // returns Map("a" -> 1, "b" -> 2) Map(1 -> "a", 2 -> "b").map((t) => t._2) // returns List("a", "b")

Eso es polimorfismo bien hecho.

Ahora, concedido, no es un paradigma convencional y ahuyentará a muchos. Pero, también atraerá a muchos que valoran su expresividad y elegancia.


Tengo un título universitario de una universidad barata del "mercado masivo" de EE. UU., Así que diría que caigo en el medio de la escala de inteligencia de usuarios (o al menos de educación) :) He estado incursionando con Scala durante unos pocos meses y han trabajado en dos o tres aplicaciones no triviales.

Especialmente ahora que IntelliJ ha lanzado su excelente IDE con lo que IMHO es actualmente el mejor complemento de Scala, el desarrollo de Scala es relativamente sencillo:

  • Encuentro que puedo usar Scala como "Java sin punto y coma", es decir, escribo código de aspecto similar a lo que haría en Java y me beneficio un poco de la brevedad sintáctica, como la que se obtiene por inferencia de tipos. El manejo de excepciones, cuando lo hago, es más conveniente. La definición de clase es mucho menos detallada sin el repetidor / instalador.

  • De vez en cuando me las arreglo para escribir una sola línea para lograr el equivalente de varias líneas de Java. Donde corresponda, las cadenas de métodos funcionales como mapear, plegar, recoger, filtrar, etc. son divertidas de componer y elegantes a la vista.

  • Solo rara vez me encuentro beneficiado con las funciones más potentes de Scala: cierres y funciones parciales (o en curry), coincidencia de patrones ... ese tipo de cosas.

Como novato, sigo luchando con la sintaxis concisa e idiomática. Las llamadas de método sin parámetros no necesitan paréntesis, excepto donde lo hacen; los casos en la declaración de coincidencia necesitan una flecha gruesa ( => ), pero también hay lugares donde se necesita una flecha delgada ( -> ). Muchos métodos tienen nombres cortos pero más bien crípticos como /: o /: - Puedo hacer mis cosas si doy la vuelta a suficientes páginas de manual, pero algunos de mis códigos terminan pareciéndose a Perl o al ruido de línea. Irónicamente, uno de los fragmentos de taquigrafía sintáctica más populares falta en la acción: me sigue mordiendo el hecho de que Int no define un método ++ .

Esta es solo mi opinión: siento que Scala tiene el poder de C ++ combinado con la complejidad y la legibilidad de C ++. La complejidad sintáctica del lenguaje también hace que la documentación de la API sea difícil de leer.

Scala está muy bien pensado y es brillante en muchos aspectos. Sospecho que a muchos académicos les encantaría programar en él. Sin embargo, también está lleno de inteligencia y problemas, tiene una curva de aprendizaje mucho más alta que Java y es más difícil de leer. Si escaneo los foros y veo cuántos desarrolladores aún están luchando con los puntos más finos de Java, no puedo imaginar que Scala se convierta en un lenguaje común . Ninguna compañía podrá justificar el envío de sus desarrolladores a un curso de Scala de 3 semanas cuando antes solo necesitaban un curso de Java de 1 semana.


Una forma en que la comunidad de Scala puede ayudar a aliviar el temor de los programadores nuevos en Scala es enfocarse en la práctica y enseñar con ejemplos: muchos ejemplos que comienzan siendo pequeños y se hacen cada vez más grandes. Aquí hay algunos sitios que tienen este enfoque:

Después de pasar un tiempo en estos sitios, rápidamente nos damos cuenta de que Scala y sus bibliotecas, aunque quizás sean difíciles de diseñar e implementar, no son tan difíciles de usar, especialmente en los casos comunes.


¿Qué pasa con los mensajes de error en el sitio de uso?

¿Y qué sucede cuando llega el caso de uso que se necesita para integrar los tipos existentes con uno personalizado que se ajuste a un DSL? Uno tiene que ser bien educado en cuestiones de asociación, precedencia, conversiones implícitas, parámetros implícitos, tipos más altos y quizás tipos existenciales.

Es muy bueno saber que en su mayoría es simple pero no necesariamente suficiente. Al menos debe haber un tipo que sepa esto si se va a diseñar una biblioteca amplia.


No sé cómo decírtelo, pero tengo un doctorado de Cambridge y estoy usando 2.8 muy bien.

Más en serio, casi no pasé el tiempo con 2.7 (no se interoperará con una biblioteca de Java que estoy usando) y comencé a usar Scala hace poco más de un mes. Tengo algo de experiencia con Haskell (no mucha), pero simplemente ignoré las cosas por las que estaba preocupado y busqué métodos que coincidieran con mi experiencia con Java (que uso para ganarme la vida).

Entonces: soy un "nuevo usuario" y no me desanimaron, el hecho de que funciona como Java me dio la confianza suficiente para ignorar los bits que no entendía.

(Sin embargo, la razón por la que estaba mirando a Scala era en parte para ver si lo presionaba en el trabajo, y no lo voy a hacer todavía. Hacer la documentación menos intimidante sin duda ayudaría, pero lo que me sorprendió es lo mucho que sigue siendo. Cambiando y desarrollándome (para ser justos, lo que más me sorprendió fue lo maravilloso que es, pero los cambios se produjeron en segundo lugar). Así que supongo que lo que estoy diciendo es que prefiero los recursos limitados para ponerlo en práctica. un estado final - no creo que esperaran ser tan populares tan pronto.)


Parece necesario indicar aquí la licenciatura: Licenciatura en Ciencias Políticas y Licenciatura en Ciencias de la Computación.

Al punto:

¿Esto va a hacer que la gente venga a Scala?

Scala es difícil, porque su paradigma de programación subyacente es difícil. La programación funcional asusta a mucha gente. Es posible construir cierres en PHP pero la gente rara vez lo hace. Así que no, no esta firma, pero todo lo demás desalentará a las personas, si no tienen la educación específica para hacer que valoren el poder del paradigma subyacente.

Si esta educación está disponible, todos pueden hacerlo. ¡El año pasado construí una computadora de ajedrez con un grupo de niños de la escuela en SCALA! Tuvieron sus problemas, pero al final lo hicieron bien.

Si estás usando Scala comercialmente, ¿estás preocupado por esto? ¿Planeas adoptar 2.8 inmediatamente o esperar a ver qué pasa?

No estaría preocupado.


¡También tengo un título en matemáticas de Oxford! Me tomó un tiempo ''obtener'' las nuevas colecciones. Pero me gusta mucho ahora que lo hago. De hecho, la tipificación de ''mapa'' fue una de las primeras grandes cosas que me molestaron en 2.7 (tal vez porque lo primero que hice fue la subclase de una de las clases de colección).

Leer el artículo de Martin sobre las nuevas colecciones 2.8 realmente ayudó a explicar el uso de implícitos, pero sí, la documentación en sí definitivamente necesita hacer un mejor trabajo para explicar el papel de diferentes tipos de implicados dentro de las firmas de métodos de las API principales.

Mi principal preocupación es más esto: ¿cuándo se lanzará la versión 2.8? ¿Cuándo dejarán de aparecer los informes de errores? ¿El equipo Scala ha mordido más de lo que puede masticar con 2.8 / ha intentado cambiar demasiado a la vez?

Realmente me gustaría ver a 2.8 estabilizado para el lanzamiento como una prioridad antes de agregar algo nuevo, y me pregunto (mientras se observa desde el margen) si se podrían hacer algunas mejoras en la forma en que se gestiona la hoja de ruta de desarrollo para el compilador de Scala.


Estoy totalmente de acuerdo tanto con la pregunta como con la respuesta de Martin :). Incluso en Java, leer javadoc con genéricos es mucho más difícil de lo que debería ser debido al ruido adicional. Esto se agrava en Scala, donde los parámetros implícitos se usan como en el código de ejemplo de las preguntas (mientras que los implícitos hacen cosas muy útiles para transformar colecciones).

No creo que sea un problema con el lenguaje en sí, creo que es más un problema de herramientas. Y aunque estoy de acuerdo con lo que dice Jörg W Mittag, creo que mirar scaladoc (o la documentación de un tipo en su IDE), debería requerir la menor capacidad cerebral posible para asimilar lo que es un método, lo que se necesita y lo que se obtiene. No debería haber necesidad de piratear un poco de álgebra en un pedazo de papel para obtenerlo :)

Por supuesto, los IDE necesitan una buena forma de mostrar todos los métodos para cualquier variable / expresión / tipo (que, como en el ejemplo de Martin, puede tener todos los genéricos en línea, por lo que es agradable y fácil de asimilar). También me gusta la idea de Martin de ocultar las implicaciones por defecto.

Para tomar el ejemplo en scaladoc ...

def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That

Cuando observo esto en scaladoc, me gustaría que el bloque genérico [B, Eso] esté oculto por defecto, así como el parámetro implícito (tal vez se muestren si pasas un pequeño icono con el mouse), como su contenido adicional. leyéndolo, lo que usualmente no es tan relevante. Por ejemplo, imagina si esto se parece a ...

def map(f: A => B): That

Agradable y claro y obvio lo que hace. Quizás se pregunte qué significa ''Eso'', si pasa el mouse por encima o hace clic en él, podría expandir el texto [B, Eso] resaltando el ''Eso'', por ejemplo.

Tal vez podría usarse un pequeño icono para la declaración [] y el bloque (implícito ...) para que quede claro que hay pequeños fragmentos de la declaración colapsada. Es difícil usar una ficha para ello, pero usaré una. por ahora...

def map.(f: A => B).: That

Entonces, por defecto, el ''ruido'' del sistema de tipos está oculto del 80% principal de lo que la gente necesita ver: el nombre del método, sus tipos de parámetros y su tipo de retorno de manera simple y concisa, con pocos enlaces ampliables al detalle. Si realmente te importa mucho.

La mayoría de las personas están leyendo scaladoc para averiguar qué métodos pueden usar en un tipo y qué parámetros pueden pasar. Estamos un poco sobrecargando a los usuarios con demasiados detalles, de la misma manera que IMHO.

Aquí hay otro ejemplo ...

def orElse[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]): PartialFunction[A1, B1]

Ahora si escondemos la declaración de genéricos es más fácil de leer

def orElse(that: PartialFunction[A1, B1]): PartialFunction[A1, B1]

Luego, si la gente se acerca, por ejemplo, A1, podríamos mostrar que A1 es A1 <: A. Los tipos covariantes y contravariantes en genéricos también agregan mucho ruido que se puede representar de una manera mucho más fácil de asimilar a los usuarios, creo.


No conozco a Scala, sin embargo, hace unas semanas no podía leer Clojure. Ahora puedo leer la mayoría, pero aún no puedo escribir nada más allá de los ejemplos más simplistas . Sospecho que Scala no es diferente. Necesitas un buen libro o curso dependiendo de cómo aprendas. Solo leyendo la declaración del mapa de arriba, obtuve tal vez 1/3.

Creo que los problemas más grandes no son la sintaxis de estos lenguajes, sino la adopción e internalización de los paradigmas que los hacen utilizables en el código de producción cotidiano. Para mí, Java no fue un gran salto de C ++, que no fue un gran salto de C, que no fue un salto en absoluto de Pascal, ni Basic, etc. Pero la codificación en un lenguaje funcional como Clojure es un gran salto yo de cualquier forma). Supongo que en Scala puedes codificar en estilo Java o estilo Scala. Pero en Clojure, creará todo el lío tratando de mantener sus hábitos imperativos de Java.


Scala tiene muchas características locas (especialmente cuando se trata de parámetros implícitos) que parecen muy complicadas y académicas, pero están diseñadas para hacer las cosas fáciles de usar. Los más útiles obtienen azúcar sintáctico (como lo [A <% B]que significa que un objeto de tipo A tiene una conversión implícita a un objeto de tipo B) y una explicación bien documentada de lo que hacen. Pero la mayoría de las veces, como cliente de estas bibliotecas, puede ignorar los parámetros implícitos y confiar en ellos para hacer lo correcto.