web-dev-qa-db-fra.com

La bibliothèque de collections Scala 2.8 est-elle un cas de "la plus longue note de suicide de l'histoire"?

Je viens juste de commencer à regarder la réimplémentation de la bibliothèque de collections Scala qui arrive dans la version imminente 2.8 . Ceux qui connaissent la bibliothèque à partir de la version 2.7 remarqueront que la bibliothèque, du point de vue de l’utilisation, a peu changé. Par exemple...

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

... fonctionnerait dans les deux versions. La bibliothèque est éminemment utilisable : en fait, c'est fantastique. Cependant, ceux qui ne connaissaient pas encore Scala et pour essayer de comprendre le langage doivent maintenant comprendre les signatures de méthodes telles que:

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

Pour une fonctionnalité aussi simple, il s’agit là d’une signature décourageante que je me suis efforcée de comprendre. Ce n'est pas que je pense que Scala soit probablement le prochain Java (ou/C/C++/C #) - je ne le fais pas Je ne crois pas que ses créateurs s’adressaient à ce marché - mais je pense que c’est/était certainement faisable pour que Scala devienne le prochain Ruby ou Python (c'est-à-dire acquérir une base d'utilisateurs commerciaux significative)

  • Est-ce que cela va dissuader les gens de venir à Scala?
  • Est-ce que cela va donner à Scala une mauvaise réputation dans le monde commercial en tant que jouet académique que seuls des doctorants dédiés peuvent comprendre? Est-ce que CTO et les responsables de logiciels vont avoir peur?
  • La bibliothèque a-t-elle repensé une idée judicieuse?
  • Si vous utilisez Scala à des fins commerciales, êtes-vous inquiet à ce sujet? Envisagez-vous d'adopter la version 2.8 immédiatement ou attendez-vous de voir ce qui se passe?

Steve Yeggene fois attaqué Scala (à tort selon moi) pour ce qu'il considérait comme son système de types trop compliqué. Je crains que quelqu'un ait une journée sur le terrain qui se propage FUD avec cette API (de la même manière que Josh Bloch a effrayé le JCP après avoir ajouté des fermetures à Java) .

Remarque - Je devrais être clair, alors que je crois que Joshua Bloch était influent dans le le rejet de la proposition de fermeture de la BGGA, je ne l’attribue à aucune autre chose que ses convictions honnêtes selon lesquelles elle représentait une erreur.


Malgré tout ce que ma femme et mes collègues me disent, je ne pense pas être un idiot: j’ai un bon diplôme en mathématiques de la niversité d’Oxford , et ma programmation est commerciale depuis presque 12 ans et dans Scala pendant environ un an (également sur le plan commercial).

Notez que le titre du sujet incendiaire est un citation faite sur le manifeste d'un parti politique britannique au début des années 1980 . Cette question est subjective mais c'est une vraie question, je l'ai faite en CW et j'aimerais avoir des opinions à ce sujet.

860
oxbow_lakes

J'espère que ce n'est pas une "note de suicide", mais je vois ce que vous voulez dire. Vous abordez ce qui est à la fois une force et un problème de Scala: sa extensibilité. Cela nous permet d'implémenter la plupart des fonctionnalités majeures dans les bibliothèques. Dans d'autres langages, des séquences avec quelque chose comme map ou collect seront intégrées, et personne ne doit voir tous les cerceaux que le compilateur doit effectuer pour qu'ils fonctionnent correctement. En Scala, tout est dans une bibliothèque et donc à l'air libre.

En fait, la fonctionnalité de map prise en charge par son type compliqué est assez avancée. Considère ceci:

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!)

Voyez comment vous obtenez toujours le meilleur type possible? Si vous associez Ints à Ints, vous obtenez à nouveau un BitSet, mais si vous associez Ints à Strings, vous obtenez un Set général. Le type statique et la représentation d'exécution du résultat de mappe dépendent du type de résultat de la fonction qui lui est transmise. Et cela fonctionne même si l'ensemble est vide, la fonction n'est donc jamais appliquée! Autant que je sache, il n'y a pas d'autre framework de collection avec une fonctionnalité équivalente. Pourtant, d’un point de vue utilisateur, c’est ainsi que les choses sont supposées fonctionner .

Le problème que nous avons, c’est que toute la technologie intelligente qui permet de le faire se répande dans les signatures de type qui deviennent volumineuses et effrayantes. Mais peut-être qu'un utilisateur ne devrait pas afficher par défaut la signature de type complète de map? Et si elle levait les yeux map dans BitSet elle avait:

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

La documentation ne se trouverait pas dans ce cas, car du point de vue de l'utilisateur, mapper a bien le type (Int => Int) => BitSet. Mais map a aussi un type plus général qui peut être inspecté en cliquant sur un autre lien.

Nous n'avons pas encore implémenté de telles fonctionnalités dans nos outils. Mais je crois que nous devons faire cela, pour ne pas effrayer les gens et pour donner des informations plus utiles. Avec de tels outils, on espère que les infrastructures et les bibliothèques intelligentes ne deviendront pas des notes de suicide.

879
Martin Odersky

Je n'ai pas de doctorat, ni aucun autre type de diplôme, ni en CS, ni en mathématiques, ni même dans aucun autre domaine. Je n'ai aucune expérience préalable de Scala ni aucun autre langage similaire. Je n'ai aucune expérience avec les systèmes de type même comparables à distance. En fait, le seul langage pour lequel j'ai plus qu'une connaissance superficielle dont même a un système de types est Pascal, n'est pas connu pour son système de types sophistiqué. (Bien que a ait des types de plage, ce qui n’est pas, AFAIK, pratiquement aucune autre langue, mais ce n’est pas vraiment pertinent ici.) Les trois autres langues que je connais sont BASIC, Smalltalk et Ruby, aucune d’entre elles. même avoir un système de type.

Et pourtant, je n'ai aucun mal à comprendre la signature de la fonction map que vous avez postée. Il me semble à peu près la même signature que map dans toutes les autres langues que j'ai jamais vues. La différence est que cette version est plus générique. Cela ressemble plus à une chose STL C++ qu'à, disons, Haskell. En particulier, elle s’éloigne du type de collection concret en n’exigeant que l’argument comme étant IterableLike, et s’abstient également du type de résultat en renvoyant en exigeant uniquement l’existence d’une fonction de conversion implicite pouvant construire quelque chose de particulier). sur cette collection de valeurs de résultat. Oui, c'est assez complexe, mais ce n'est vraiment que l'expression du paradigme général de la programmation générique: n'assumez rien de ce que vous n'avez pas réellement à faire.

Dans ce cas, map ne fait pas besoin la collection ne doit pas être une liste, ni être ordonnée, ni être triable ou quelque chose du genre. La seule chose qui compte pour map est qu’elle peut accéder à tous les éléments de la collection, les uns après les autres, mais sans ordre particulier. Et il n'a pas besoin de savoir quelle est la collection résultante, il suffit de savoir comment la construire. Donc, c’est ce que requiert sa signature de type.

Donc, au lieu de

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

qui est la signature de type traditionnelle pour map, il est généralisé de ne pas exiger une structure concrète List mais plutôt une structure IterableLike

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

qui est ensuite généralisé en ne demandant qu’une fonction qui puisse convertir le résultat en n’importe quelle structure de données souhaitée par l’utilisateur:

map :: IterableLike i ⇒ (a → b) → i → ([b] → c) → c

J'admets que la syntaxe est un peu plus complexe, mais la sémantique est la même. Fondamentalement, cela commence à partir de

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

qui est la signature traditionnelle pour map. (Notez comment, en raison de la nature orientée objet de Scala, le paramètre de liste d'entrées disparaît, car il s'agit désormais du paramètre de récepteur implicite que possède chaque méthode dans un système à une seule expédition OO.) Il a ensuite généralisé à partir de un concret List à un plus général IterableLike

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

Maintenant, il remplace la collection de résultats IterableLike par une fonction qui produit, eh bien, à peu près tout.

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

Ce que je crois vraiment n'est pas ça difficile à comprendre. Il n’ya vraiment que quelques outils intellectuels dont vous avez besoin:

  1. Vous devez savoir (en gros) ce que map est. Si vous avez donné niquement la signature de type sans le nom de la méthode, je l'avoue, il serait beaucoup plus difficile de comprendre ce qui se passe. Mais puisque vous avez déjà savez ce que map est censé faire et que vous savez quelle est sa signature de type, vous pouvez rapidement numériser la signature et vous concentrer sur les anomalies, comme "pourquoi Est-ce que map prend deux fonctions en arguments, pas une? "
  2. Vous devez être capable de réellement lire la signature de type. Mais même si vous n'avez jamais vu Scala, cela devrait être assez facile, car il s'agit simplement d'un mélange de syntaxes que vous connaissez déjà dans d'autres langues: VB.NET utilise des crochets pour le polymorphisme paramétrique, et utiliser une flèche pour indiquer le type de retour et deux points pour séparer le nom et le type est en fait la norme.
  3. Vous devez savoir à peu près ce qu'est la programmation générique. (Ce qui n'est pas ça difficile à comprendre, puisque tout est énoncé dans le nom: il s'agit littéralement de programmer de manière générique).

Aucun de ces trois ne devrait donner à un programmeur professionnel ou même amateur un mal de tête sérieux. map est une fonction standard de presque toutes les langues conçues au cours des 50 dernières années. Le fait que différentes langues utilisent une syntaxe différente devrait être évident pour quiconque a conçu un site Web avec HTML et CSS sans abonnement. à une liste de diffusion, même à distance, liée à la programmation, sans quelque ennuyeux fanboy C++ de l’église de St Stepanov, expliquant les vertus de la programmation générique.

Oui, Scala est complexe. Oui, Scala possède l'un des systèmes de types les plus sophistiqués connus, rivalisant et même dépassant les langages tels que Haskell, Miranda, Clean ou Cyclone. Mais si la complexité était un argument contre le succès d’un langage de programmation, C++ serait mort depuis longtemps et nous écririons tous Scheme. Il y a de nombreuses raisons pour lesquelles Scala n'aura probablement pas de succès, mais le fait que les programmeurs ne soient pas dérangés pour se mettre la tête avant de s'asseoir devant le clavier ne sera probablement pas le problème. un principal.

223
Jörg W Mittag

Même chose 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;
}
172
skyde

Eh bien, je peux comprendre votre douleur, mais très franchement, les gens comme vous et moi - ou pratiquement tous les utilisateurs normaux de Stack Overflow - ne sont pas la règle.

Ce que je veux dire par là, c'est que ... la plupart des programmeurs ne se soucieront pas de cette signature, car ils ne les verront jamais! Ils ne lisent pas la documentation.

Tant qu'ils verront un exemple du fonctionnement du code et qu'il ne leur manquera pas de produire le résultat qu'ils attendez, ils ne consulteront jamais la documentation. En cas d'échec, ils consulteront la documentation et s'attendent à voir exemples d'utilisation en haut.

En pensant à cela, je pense que:

  1. Quiconque (comme dans la plupart des gens) qui rencontrent cette signature dactylographique se moquera de Scala sans préjugés s'ils sont prédisposés, et le considérera comme un symbole du pouvoir de Scala s'ils aiment Scala.

  2. Si la documentation n'est pas améliorée pour fournir des exemples d'utilisation et expliquer clairement à quoi sert une méthode et comment l'utiliser, cela peut nuire un peu à Scala adoption.

  3. À long terme, ce ne sera pas grave. Ce Scala can faire ce genre de choses rendra les bibliothèques écrites pour Scala beaucoup plus puissantes et plus sûres. Ces bibliothèques et frameworks attireront les programmeurs attirés par des outils puissants.

  4. Les programmeurs qui aiment la simplicité et la simplicité continueront à utiliser PHP ou des langages similaires.

Hélas, les programmeurs Java ont une grande passion pour les outils électriques, alors, pour répondre à cette question, je viens de réviser mes attentes en matière d'adoption Scalaprincipale. Je ne doute pas du tout que Scala deviendra une langue dominante. Pas C-mainstream, mais peut-être Perl-mainstream ou PHP-mainstream.

En parlant de Java, avez-vous déjà remplacé le chargeur de classe? Avez-vous déjà examiné ce que cela implique? Java peut être effrayant, si vous regardez les endroits que font les auteurs de framework. C'est juste que la plupart des gens ne le font pas. La même chose s’applique à Scala, à mon humble avis, mais les premiers utilisateurs ont tendance à regarder sous chaque rocher qu’ils rencontrent pour voir s’il ya quelque chose de caché.

71
Daniel C. Sobral

Est-ce que cela va dissuader les gens de venir à Scala?

Oui, mais cela empêchera également les gens d'être rebutés. J'ai considéré le manque de collections utilisant des types plus élevés comme une faiblesse majeure depuis que Scala a gagné le soutien des types plus élevés. Cela complique la documentation de l'API, mais rend l'utilisation plus naturelle.

Est-ce que cela va donner à scala une mauvaise réputation dans le monde commercial en tant que jouet académique que seuls des doctorants dédiés peuvent comprendre? Les directeurs techniques et les responsables logiciels vont-ils avoir peur?

Certains le feront probablement. Je ne pense pas que Scala soit accessible à de nombreux développeurs "professionnels", en partie à cause de la complexité de Scala et en partie à cause de la réticence de nombreux développeurs à apprendre. Les CTO qui emploient de tels développeurs seront à juste titre effrayés.

La bibliothèque a-t-elle repensé une idée judicieuse?

Absolument. Cela rend les collections bien mieux adaptées au reste de la langue et au système de typographie, même si elle présente encore quelques aspérités.

Si vous utilisez scala dans le commerce, êtes-vous inquiet à ce sujet? Envisagez-vous d'adopter la version 2.8 immédiatement ou attendez-vous de voir ce qui se passe?

Je ne l'utilise pas commercialement. J'attendrai probablement au moins quelques tours dans la série 2.8.x avant même d'essayer de l'introduire afin que les bogues puissent être éliminés. J'attendrai également de voir à quel point l'EPFL a réussi à améliorer ses processus de développement et de publication. Ce que je vois a l'air d'espérer, mais je travaille pour une entreprise conservatrice.

Le sujet plus général de "Est-ce que Scala est trop compliqué pour les développeurs traditionnels?" ...

La plupart des développeurs, grand public ou non, entretiennent ou étendent des systèmes existants. Cela signifie que la plupart de ce qu'ils utilisent est dicté par des décisions prises il y a longtemps. Il y a encore beaucoup de gens qui écrivent en COBOL.

Le principal développeur de demain s’occupera de la maintenance et de l’extension des applications en cours de construction. Bon nombre de ces applications ne sont pas conçues par les développeurs grand public. Les développeurs traditionnels de demain utiliseront le langage utilisé par les développeurs les plus performants de nouvelles applications.

55
Erik Engbrecht

Une des façons dont la communauté Scala peut aider à apaiser la peur des programmeurs débutants pour Scala est de se concentrer sur la pratique et d'enseigner par l'exemple - de nombreux exemples qui commencent modestement et grandissent progressivement plus grande. Voici quelques sites qui adoptent cette approche:

Après avoir passé quelque temps sur ces sites, on se rend vite compte que Scala et ses bibliothèques, bien que difficiles à concevoir et à mettre en œuvre, ne sont pas si difficiles à utiliser, surtout dans les cas les plus courants.

46
Derek Mahar

J'ai un diplôme de premier cycle d'une université américaine bon marché "grand public", alors je dirais que je tombe dans le milieu de l'échelle d'intelligence de l'utilisateur (ou du moins de l'éducation) :) J'ai bécoté avec Scala depuis quelques mois seulement et ont travaillé sur deux ou trois applications non triviales.

Surtout maintenant que IntelliJ a publié son amende IDE avec ce que IMHO est actuellement le meilleur plugin Scala, le développement Scala est relativement simple:

  • Je trouve que je peux utiliser Scala comme "Java sans point-virgule", c'est-à-dire que j'écris un code similaire à ce que je ferais en Java et que je bénéficierai un peu de la brièveté syntaxique telle que celle obtenue par l'inférence de type. . La gestion des exceptions, quand je le fais du tout, est plus pratique. La définition de classe est beaucoup moins verbeuse sans getter/setter.

  • De temps en temps, je parviens à écrire une seule ligne pour obtenir l'équivalent de plusieurs lignes de Java. Le cas échéant, les chaînes de méthodes fonctionnelles telles que la carte, le pliage, la collecte, le filtrage, etc. sont amusantes à composer et élégantes à regarder.

  • Ce n'est que rarement que je profite des fonctions plus puissantes de Scala: fermetures et fonctions partielles (ou curry), correspondance de modèle ... ce genre de chose.

En tant que novice, je continue à lutter avec la syntaxe concise et idiomatique. Les appels de méthode sans paramètres n'ont pas besoin de parenthèses, sauf là où ils le sont. les cas dans l'instruction de correspondance ont besoin d'une grosse flèche (=>), mais il existe également des endroits où vous avez besoin d'une fine flèche (->). Beaucoup de méthodes ont des noms courts mais plutôt cryptiques comme /: ou \: - Je peux terminer mon travail si je retourne suffisamment de pages de manuel, mais certains de mes codes finissent par ressembler à Perl ou à du bruit de ligne. Ironiquement, il manque l'un des raccourcis syntaxiques les plus populaires: je continue à me faire piquer par le fait que Int ne définit pas de méthode ++.

Ceci n’est que mon opinion: j’ai l’impression que Scala possède la puissance du C++, associée à la complexité et à la lisibilité de ce dernier. La complexité syntaxique du langage rend également difficile la lecture de la documentation de l'API.

Scala est très bien pensé et brillant à bien des égards. Je pense que de nombreux universitaires aimeraient y programmer. Cependant, il est aussi plein d’ingéniosité et de pièges, il a une courbe d’apprentissage beaucoup plus longue que Java et est plus difficile à lire. Si je balaye les forums et vois combien de développeurs ont encore du mal à maîtriser les subtilités de Java, je ne peux pas concevoir que Scala devienne jamais un langage courant. Aucune entreprise ne pourra justifier d’envoyer ses développeurs à un cours de 3 semaines Scala alors qu’ils n’avaient auparavant besoin que d’un cours d’une semaine Java.

43
Carl Smotricz

Je pense que le problème principal avec cette méthode est que le (implicit bf : CanBuildFrom[Repr, B, That]) va sans explication. Même si je connais les arguments implicites, rien n'indique en quoi cela affecte l'appel. Traverser le scaladoc ne fait que me rendre plus confus (peu de classes liées à CanBuildFrom ont même de la documentation).

Je pense qu'un simple "il doit y avoir un objet implicite dans la portée de bf qui fournit un générateur pour les objets de type B dans le type de retour That" aiderait quelque peu, mais c'est un peu un concept enivrant quand tout ce que vous voulez faire est de mapper les A à B. En fait, je ne suis pas sûr que ce soit exact, car je ne sais pas ce que signifie le type Repr, et la documentation de Traversable ne donne certainement aucun indice.

Donc, il ne me reste que deux options, ni plaisantes:

  • Supposons que cela fonctionne simplement comment l'ancienne carte fonctionne et comment elle fonctionne dans la plupart des autres langues
  • Creusez un peu plus dans le code source

Je comprends que Scala expose essentiellement les entrailles de la façon dont ces choses fonctionnent et qu’il offre en définitive un moyen de faire ce que décrit oxbow_lakes. Mais c'est une distraction dans la signature.

32
davetron5000

Je suis un débutant Scala et honnêtement, je ne vois pas d'inconvénient à ce type de signature. Le paramètre est la fonction à mapper et le paramètre implicite, le générateur qui renvoie la collection correcte. Clair et lisible.

Le tout est assez élégant, en fait. Les paramètres de type de générateur permettent au compilateur de choisir le type de retour correct, tandis que le mécanisme de paramètre implicite masque ce paramètre supplémentaire à l'utilisateur de classe. J'ai essayé ceci:

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")

C'est du polymorphisme bien fait.

Maintenant, d'accord, ce n'est pas un paradigme grand public et il en effraiera beaucoup. Mais, il attirera également beaucoup de gens qui apprécient son expressivité et son élégance.

22
Thomas Heywood

Malheureusement, la signature que vous avez donnée pour la carte est incorrecte et il y a en effet une critique légitime.

La première critique est qu'en subvertissant la signature de la carte, nous obtenons quelque chose de plus général. C'est une erreur commune de croire que c'est une vertu par défaut. Ce n'est pas. La fonction de la carte est très bien définie comme un foncteur covariant Fx -> (x -> y) -> Fy respectant les deux lois de la composition et de l'identité. Tout ce qui est attribué à "carte" est une parodie.

La signature donnée est quelque chose d'autre, mais ce n'est pas une carte. Ce que je soupçonne qu’il essaie d’être, c’est une version spécialisée et légèrement modifiée de la signature "traverse" du document, L’essence du motif Iterator. Voici sa signature:

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

Je vais le convertir en Scala:

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

Bien sûr, cela échoue - ce n'est pas assez général! En outre, il est légèrement différent (notez que vous pouvez obtenir une carte en traversant le foncteur Identity). Cependant, je soupçonne que si les rédacteurs de bibliothèque étaient plus au courant des généralisations de bibliothèque bien documentées (la programmation applicative avec effets précède ce qui précède), nous ne verrions pas cette erreur.

Deuxièmement, la fonction map est un cas particulier dans Scala en raison de son utilisation dans les for-compréhensions. Cela signifie malheureusement qu'un concepteur de bibliothèque mieux équipé ne peut ignorer cette erreur sans sacrifier le sucre syntaxique de la compréhension. En d'autres termes, si les concepteurs de la bibliothèque Scala devaient détruire une méthode, cette opération est facilement ignorée, mais veuillez ne pas mapper!

J'espère que quelqu'un en parlera, car, dans l'état actuel des choses, il deviendra de plus en plus difficile de contourner les erreurs que Scala insiste fortement à faire, apparemment pour des raisons pour lesquelles j'ai de fortes objections. C'est-à-dire que la solution aux "objections irresponsables du programmeur moyen (c'est-à-dire trop difficile!)" N'est pas "de l'apaiser pour le leur faciliter la tâche" mais plutôt de fournir des conseils et une assistance pour devenir de meilleurs programmeurs. Mes objectifs et ceux de Scala sont en conflit sur cette question, mais revenons à votre propos.

Vous faisiez probablement votre point, en prévoyant des réponses spécifiques du "programmeur moyen". C'est-à-dire les gens qui vont réclamer "mais c'est trop compliqué!" ou quelque chose comme ça. Ce sont les Yegges ou Blochs auxquels vous vous référez. Ma réponse à ces gens du mouvement anti-intellectuel/pragmatiste est assez dure et je prévois déjà un barrage de réponses, alors je vais l'omettre.

J'espère sincèrement que les bibliothèques Scala s'amélioreront, ou du moins que les erreurs pourront être cachées dans un coin. Java est un langage dans lequel "essayer de faire quelque chose d'utile" est incroyablement coûteux, ce qui en vaut souvent la peine, car la très grande majorité des erreurs ne peuvent tout simplement pas être évitées. J'implore Scala de ne pas suivre le même chemin.

20
Tony Morris

Je suis totalement d'accord avec la question et la réponse de Martin :). Même en Java, la lecture de javadoc avec des génériques est beaucoup plus difficile qu’elle ne le devrait en raison du bruit supplémentaire. Ceci est composé dans Scala où les paramètres implicites sont utilisés comme dans l'exemple de code de la question (alors que les implicites font des choses très utiles pour le morphing de collection).

Je ne pense pas que ce soit un problème avec la langue en soi, mais plutôt un problème d’outillage. Et bien que je sois d’accord avec ce que dit Jörg W Mittag, je pense que regarder scaladoc (ou la documentation d’un type dans votre IDE) devrait exiger le moins de puissance cérébrale possible pour définir la méthode, ce qu’elle prend et ce qui retourne. Il ne devrait pas être nécessaire de pirater un peu d'algèbre sur un bout de papier pour l'obtenir :)

Bien sûr, les IDE ont besoin d’un moyen agréable d’afficher toutes les méthodes de toutes les variables/expressions/types (qui, comme dans l’exemple de Martin, peut avoir tous les génériques en ligne, ce qui en fait un outil agréable et facile à utiliser). J'aime l'idée de Martin de cacher les implications par défaut aussi.

Pour prendre l'exemple de scaladoc ...

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

Dans scaladoc, j'aimerais que le bloc générique [B, That] soit masqué par défaut, ainsi que le paramètre implicite (peut-être qu'ils indiquent si vous survolez une petite icône avec la souris) - comme élément supplémentaire à suivre en le lisant, ce qui n'est généralement pas pertinent. par exemple. imaginez si cela ressemblait à ...

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

Beau et clair et évident ce qu'il fait. Vous vous demandez peut-être ce que c'est, si vous passez la souris dessus ou si vous cliquez dessus, cela pourrait développer le texte [B, That] en soulignant le "Cela" par exemple.

Peut-être qu'une petite icône pourrait être utilisée pour la déclaration [] et le bloc (implicite ...) afin qu'il soit clair qu'il y a des petits morceaux de la déclaration effondrée? Il est difficile d’utiliser un jeton pour cela, mais je vais utiliser un. pour l'instant...

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

Donc, par défaut, le 'bruit' du système de types est caché aux 80% principaux de ce que les gens doivent regarder - le nom de la méthode, ses types de paramètres et son type de retour dans une manière simple et concise de Nice - avec peu de liens extensibles vers le détail si vous vous en souciez vraiment beaucoup.

La plupart du temps, les gens lisent scaladoc pour savoir quelles méthodes ils peuvent appeler et quels paramètres ils peuvent transmettre. Nous surchargons les utilisateurs avec trop de détails, à mon humble avis.

Voici un autre exemple ...

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

Maintenant, si nous avons caché la déclaration des génériques, il est plus facile à lire

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

Ensuite, si les gens survolent, disons, A1, nous pourrions montrer que A1 est A1 <: A. Les types covariants et contravariants dans les génériques ajoutent aussi beaucoup de bruit qui peut être rendu beaucoup plus facile pour les utilisateurs, je pense.

15
James Strachan

Je ne sais pas comment vous le dire, mais j'ai un doctorat de Cambridge et j'utilise très bien la version 2.8.

Plus sérieusement, j'ai à peine passé tout le temps avec 2.7 (cela n'interagit pas avec une bibliothèque Javaque j'utilise) et j'ai commencé à utiliser Scala il y a un peu plus d'un mois. J'ai quelques expériences avec Haskell (pas grand-chose), mais j'ai simplement ignoré les choses qui vous inquiètent et recherché des méthodes correspondant à mon expérience avec Java (que j'utilise pour gagner ma vie).

Donc: je suis un "nouvel utilisateur" et je n’ai pas été rebuté - le fait que cela fonctionne comme si [Java] _ me donnait assez de confiance pour ignorer les passages que je ne comprenais pas.

(Cependant, la raison pour laquelle j’examinais Scala était en partie pour voir si je devais faire pression au travail et je ne vais pas le faire pour le moment. Rendre la documentation moins intimidante aiderait certainement, mais ce qui m’a surpris C’est à quel point les choses évoluent et se développent (pour être juste, ce qui m’a le plus surpris, c’est l’impressionnant, mais les changements se sont succédé en deuxième position.) Je suppose donc que ce que je dis, c’est que je préfère les ressources limitées. ont été mis dans l’obtention d’un état final - je ne pense pas qu’ils s’attendaient à devenir aussi populaire aussi vite.)

11
andrew cooke

Je ne sais pas du tout Scala, mais il y a quelques semaines, je ne pouvais pas lire Clojure. Maintenant, je peux en lire l'essentiel, mais je ne peux rien écrire au-delà des exemples les plus simplistes . Je soupçonne que Scala n'est pas différent. Vous avez besoin d'un bon livre ou d'un bon cours en fonction de votre apprentissage. En lisant juste la déclaration carte ci-dessus, j'en ai peut-être 1/3.

Je pense que les problèmes les plus importants ne sont pas la syntaxe de ces langages, mais l’adoption et l’internalisation des paradigmes qui les rendent utilisables dans le code de production quotidien. Pour moi, Java n'était pas un énorme bond en avant de C++, ni un énorme bond en avant de C, ni du saut de Pascal, ni de Basic etc ... Mais coder dans un langage fonctionnel comme Clojure est un énorme saut (pour moi en tout cas). Je suppose que dans Scala, vous pouvez coder dans le style Java ou le style Scala. Mais dans Clojure, vous allez créer tout le bazar en essayant de garder vos habitudes impératives de Java.

10
Jeff G

Scala a beaucoup de fonctionnalités délirantes (particulièrement en ce qui concerne les paramètres implicites) qui ont l’air très compliquées et académiques, mais qui sont conçues pour rendre les choses faciles à utiliser. Les plus utiles ont un sucre syntaxique (comme [A <% B] qui signifie qu'un objet de type A a une conversion implicite en un objet de type B) et une explication bien documentée de ce qu'ils font. Mais la plupart du temps, en tant que client de ces bibliothèques, vous pouvez ignorer les paramètres implicites et leur faire confiance pour agir correctement.

7
Ken Bloom

Est-ce que cela va dissuader les gens de venir à Scala?

Je ne pense pas que ce soit le principal facteur qui affectera la popularité de Scala, parce que Scala a beaucoup de pouvoir et que sa syntaxe n'est pas aussi étrangère à Java/C++./Programmeur PHP comme Haskell, OCaml, SML, Lisps, etc.

Mais je pense effectivement que la popularité de Scala sera inférieure à celle où se trouve Java aujourd'hui, car je pense également que le prochain langage traditionnel doit être beaucoup simplifié et que le seul moyen d'y parvenir est la pure immuabilité, c'est-à-dire déclarative. comme HTML, mais Turing complète. Cependant, je suis partial parce que je développe un tel langage, mais je ne l’ai fait qu’après une étude de plusieurs mois selon laquelle Scala ne pourrait pas suffire à ce dont j'avais besoin.

Est-ce que cela va donner à Scala une mauvaise réputation dans le monde commercial en tant que jouet académique que seuls des doctorants dévoués peuvent comprendre? Les directeurs techniques et les responsables logiciels vont-ils avoir peur?

Je ne pense pas que la réputation de Scala va souffrir du complexe Haskell. Mais je pense que certains retarderont l’apprentissage parce que, pour la plupart des programmeurs, je ne vois pas encore de scénario d’usage qui les obligerait à utiliser Scala, et ils tergiverseront l’apprentissage à ce sujet. Le côté serveur hautement évolutif est peut-être le cas d'utilisation le plus convaincant.

Et, pour le marché grand public, le premier apprentissage Scala n'est pas une "bouffée d'air frais", où l'on écrit immédiatement des programmes, tels que l'utilisation préalable de HTML ou de Python. Scala a tendance à grandir sur vous, après avoir appris tous les détails que l'on trébuche depuis le début. Cependant, peut-être que si j'avais lu la programmation dans Scala dès le début, mon expérience et mon opinion sur la courbe d'apprentissage auraient été différentes.

La bibliothèque a-t-elle repensé une idée judicieuse?

Absolument.

Si vous utilisez Scala à des fins commerciales, êtes-vous inquiet à ce sujet? Envisagez-vous d'adopter la version 2.8 immédiatement ou attendez-vous de voir ce qui se passe?

J'utilise Scala comme plateforme initiale de ma nouvelle langue. Je ne construirais probablement pas de code dans la bibliothèque de collections de Scala si j'utilisais Scala à des fins commerciales autrement. Je créerais ma propre bibliothèque basée sur la théorie des catégories, puisqu’une fois, j’ai trouvé les signatures de types de Scalaz encore plus verbeuses et lourdes que la bibliothèque de collections de Scala. Une partie de ce problème tient peut-être à la manière dont Scala a implémenté les classes de types, et c’est une raison mineure pour laquelle je crée mon propre langage.


J'ai décidé d'écrire cette réponse parce que je voulais me forcer à rechercher et à comparer la conception de la classe de collection de Scala à celle que je suis en train de faire pour ma langue. Pourrais aussi bien partager mon processus de pensée.

L'utilisation de 2.8 Scala collections d'une abstraction de constructeur est un principe de conception solide. Je veux explorer deux compromis de conception ci-dessous.

  1. CODE ECRIVE UNIQUEMENT: Après avoir écrit cette section, je lis commentaire de Carl Smotricz qui est en accord avec ce que je prévois être le compromis. Les commentaires de James Strachan et davetron5000 s'accordent pour dire que le sens de That (ce n'est même pas That [B]) et le mécanisme de l'implicite ne sont pas faciles à saisir intuitivement. Voir mon utilisation du monoïde dans le numéro 2 ci-dessous, ce qui me semble beaucoup plus explicite. Le commentaire de Derek Mahar concerne l’écriture de Scala, mais qu’en est-il de la lecture du Scala d’autres qui n’est pas "dans les cas courants".

    Une des critiques que j'ai lues à propos de Scala est qu'il est plus facile de l'écrire que de lire le code écrit par d'autres. Et je trouve que cela est parfois vrai pour diverses raisons (par exemple, de nombreuses façons d'écrire une fonction, des fermetures automatiques, des unités pour les DSL, etc.), mais je suis indécis s'il s'agit d'un facteur majeur. Ici, l'utilisation de paramètres de fonction implicites a des avantages et des inconvénients. Sur le plan positif, il réduit la verbosité et automatise la sélection de l'objet générateur. Dans Odersky exemple , la conversion d'un BitSet, c'est-à-dire Set [Int], en Set [String] est implicite. Le lecteur peu familier du code risque de ne pas savoir facilement quel type de collection est constitué, à moins de pouvoir bien raisonner sur tous les potentiels candidats constructeurs implicites invisibles qui pourraient exister dans la portée du package actuel. Bien sûr, le programmeur expérimenté et le rédacteur du code sauront que BitSet est limité à Int. Par conséquent, une carte en chaîne doit être convertie en un type de collection différent. Mais quel type de collection? Ce n'est pas spécifié explicitement.

  2. CONCEPTION DE COLLECTION AD-HOC: Après avoir écrit cette section, j'ai lu commentaire de Tony Morris et réalisé que je fais presque le même argument. Peut-être que mon exposé plus détaillé expliquera mieux la situation.

    Dans "Combattre la pourriture avec les types" Odersky & Moors, deux cas d'utilisation sont présentés. Il s'agit de la restriction des éléments BitSet sur les éléments Int et de Mapper pour lier les éléments Tuple, et sont fournies comme la raison pour laquelle la fonction de mappage d'élément générale, A => B, doit pouvoir générer d'autres types de collection de destination. Cependant, cela est peut-être imparfait du point de vue de la théorie des catégories. Pour être cohérent dans la théorie des catégories et ainsi éviter les angles, ces types de collection sont des foncteurs dans lesquels chaque morphisme, A => B, doit mapper entre des objets appartenant à la même catégorie de foncteurs, List [A] => List [B], BitSet. [A] => BitSet [B]. Par exemple, une option est un foncteur qui peut être vu comme une collection d’ensembles d’un objet (Some) et d’un objet. Il n’existe pas de carte générale de Option (aucun) ou de liste (Nil) d’option aux autres foncteurs qui n’ont pas un état "vide".

    Il y a un choix de conception de compromis fait ici. Dans la bibliothèque de plans pour les collections de mon nouveau langage, j’ai choisi de tout transformer en foncteur, c’est-à-dire que si je mets en œuvre un BitSet, il doit prendre en charge tous les types d’élément, en utilisant une représentation interne de champ non binaire lorsqu’un modèle non type de paramètre entier, et cette fonctionnalité est déjà dans le jeu dont elle hérite dans Scala. Et Mapper dans ma conception doit mapper uniquement ses valeurs et peut fournir une méthode distincte sans foncteur pour mapper ses n-uplets de paires (clé, valeur). Un avantage est que chaque foncteur est alors généralement aussi un applicatif et peut-être aussi une monade. Ainsi, toutes les fonctions entre types d’éléments, par ex. A => B => C => D => ..., sont automatiquement levés vers les fonctions entre les types d’applications levés, par ex. Liste [A] => Liste [B] => Liste [C] => Liste [D] => .... Pour mapper un foncteur sur une autre classe de collection, je propose une surcharge de carte prenant un monoïde, par exemple. Nil, None, 0, "", Array (), etc. La fonction d'abstraction de générateur est donc la méthode append d'un monoïde. Elle est fournie explicitement en tant que paramètre d'entrée nécessaire, donc sans conversions implicites invisibles. (Tangent: ce paramètre d'entrée permet également l'ajout de monoïdes non vides, ce que la conception de carte de Scala ne peut pas faire.) De telles conversions sont une carte et un repli dans la même passe d'itération. Je propose également une "programmation applicative avec effets" qui peut être parcourue, dans le sens de la catégorie, McBride & Patterson, qui permet également un repliement de carte en un seul passage d'itération, de n'importe quel objet traversable à tout type d'application, où la plupart des classes de collection sont les deux. De plus, la monade d’état est une application et constitue donc une abstraction de constructeur totalement généralisée de tout objet pouvant être parcouru.

    Ainsi, les collections Scala sont "ad-hoc" dans le sens où elles ne sont pas fondées sur la théorie des catégories et la théorie des catégories est l'essence même de la sémantique dénotationnelle de niveau supérieur. Bien que les constructeurs implicites de Scala aient au départ une apparence "plus généralisée" qu'un modèle de foncteur + un constructeur monoïde + traversable -> applicatif, ils ne sont peut-être pas compatibles avec aucune catégorie, et nous ne savons donc pas quelles règles ils suivent dans la liste. le sens le plus général et ce que les cas critiques seront donnés, ils ne peuvent obéir à aucun modèle de catégorie. Il n’est tout simplement pas vrai que l’ajout de plus de variables donne un caractère plus général. C’est l’un des grands avantages de la théorie des catégories, c’est-à-dire qu’il fournit des règles permettant de conserver la généralité tout en passant à la sémantique de niveau supérieur. Une collection est une catégorie.

    J'ai lu quelque part, je pense que c'était Odersky, une autre justification de la conception de la bibliothèque, est que la programmation dans un style purement fonctionnel a le coût d'une récursivité limitée et de la vitesse lorsque la récursion par la queue n'est pas utilisée. Je n'ai pas trouvé difficile d'employer la récursion de la queue dans tous les cas que j'ai rencontrés jusqu'à présent.


De plus, je garde dans mon esprit une idée incomplète du fait que certains des compromis de Scala sont dus au fait d’essayer d’être à la fois une langue mutable et immuable, contrairement à Haskell ou à la langue que je suis en train de développer. Cela concorde avec le commentaire de Tony Morris sur les compréhensions. Dans mon langage, il n'y a pas de boucles ni de constructions mutables. Ma langue sera au-dessus de Scala (pour le moment) et doit beaucoup à cela. Ce ne serait pas possible si Scala n'avait pas le système de type général ni la mutabilité. Cela n’est peut-être pas vrai, parce que je pense qu'Odersky & Moors ("Combattre la pourriture avec des types") est incorrect pour indiquer que Scala est le seul langage OOP avec des types supérieurs, parce que j’ai vérifié (avec Bob Harper et moi-même) que Standard ML les possède. Il semble également que le système de types de SML puisse être aussi flexible (depuis les années 1980), ce qui n’est peut-être pas facile à comprendre car la syntaxe n’est pas tellement similaire à Java (et C++/PHP) à Scala. En tout état de cause, il ne s’agit pas d’une critique de Scala, mais bien d’une tentative de présenter une analyse incomplète des compromis, ce qui, j’espère, a rapport à la question. Scala et SML ne souffrent pas de l'incapacité de Haskell à faire héritage multiple de diamant , ce qui est essentiel et je comprends pourquoi nombre de fonctions du prélude Haskell sont répétées pour différents types.

6
Shelby Moore III

Il semble nécessaire d’indiquer ici son degré: B.A. en sciences politiques et diplômé en informatique.

Jusqu'au point:

Est-ce que cela va dissuader les gens de venir à Scala?

Scala est difficile, car son paradigme de programmation sous-jacent est difficile. La programmation fonctionnelle fait peur à beaucoup de gens. Il est possible de construire des fermetures dans PHP mais les gens le font rarement. Donc non, pas cette signature, mais tout le reste va rebuter les gens s'ils ne possèdent pas l'éducation spécifique leur permettant de valoriser le pouvoir du paradigme sous-jacent.

Si cette éducation est disponible, tout le monde peut le faire. L'année dernière, j'ai construit un ordinateur d'échecs avec un groupe d'écoliers à SCALA! Ils ont eu leurs problèmes mais ils ont bien réussi à la fin.

Si vous utilisez Scala à des fins commerciales, cela vous inquiète-t-il? Envisagez-vous d'adopter la version 2.8 immédiatement ou attendez-vous de voir ce qui se passe?

Je ne serais pas inquiet.

5
Julian Dehne

J'ai aussi un diplôme en mathématiques d'Oxford! Il m'a fallu un certain temps pour "obtenir" les nouvelles collections. Mais j'aime beaucoup maintenant que je le fais. En fait, le typage de 'map' a été l'une des premières choses qui m'a dérangé dans la 2.7 (peut-être depuis que j'ai tout d'abord créé la sous-classe de l'une des classes de la collection).

La lecture du document de Martin sur les nouvelles collections 2.8 a vraiment aidé à expliquer l'utilisation des implicites, mais la documentation elle-même doit certainement mieux expliquer le rôle des différents types d'implications dans les signatures de méthodes des API principales.

Ma principale préoccupation est davantage la suivante: quand la version 2.8 sera-t-elle publiée? Quand les rapports de bugs cesseront-ils d'arriver? avez-vous scala l'équipe mordue plus qu'elle ne peut mâcher avec 2.8/essayé de trop changer en même temps?

J'aimerais vraiment que la stabilisation de la version 2.8 devienne une priorité avant d'ajouter quoi que ce soit de nouveau, et je me demande (en regardant de côté) si des améliorations pourraient être apportées à la façon dont la feuille de route de développement pour la scala le compilateur est géré.

4
Matt