Scala (llenguatge de programació)

De Viquipèdia
Salta a: navegació, cerca
Infotaula de llenguatge de programacióScala (llenguatge de programació)
Ј2.png
Tipus llenguatge de programació orientat a objectes, llenguatge de programació funcional i llenguatge de programació multiparadigma
Data de creació 2003
Disseny Martin Odersky
Desenvolupador Martin Odersky
Darrera versió estable 2.12.1
Llenguatge de programació Scala
Influenciat per ML, Java, Erlang, Haskell, Smalltalk, ML Estàndard i OCaml
Llicència Llicència BSD
Etiqueta d'Stack Exchange https://stackoverflow.com/tags/scala
Pàgina web Web oficial
Modifica dades a Wikidata

Scala és un llenguatge de programació modern multi-paradigma dissenyat per a expressar patrons de programació generals d'una manera concisa, elegant i segura respecte als tipus. Integra característiques d'orientació a objectes i de llenguatges funcionals.

Contingut

Scala és orientat a objectes[modifica | modifica el codi]

Scala és un llenguatge pur O.O. (orientat a objectes) en el sentit que cada valor és un objecte. Els tipus i comportaments dels objectes són descrits mitjançant classes i 'trets'. L'abstracció classe s'estén mitjançant subclasses i també amb un mecanisme flexible basat en classes 'mixins' (barrejables: similars als interfaces de Java però amb definició de tipus, variables i implementació de mètodes) anomenades trets com a substitució neta de l'herència múltiple.

Scala és funcional[modifica | modifica el codi]

Scala també és un llenguatge funcional en el sentit que cada funció és un valor. Scala proveeix una sintaxi fàcil per a la definició de funcions anònimes, suporta funcions d'ordre superior, permet la imbricació de funcions i suporta el currying. Les classes per cas (ang.: case classes) i el suport que inclou de reconeixement de patrons, serveix per a modelar tipus algebraics, emprats en molts llenguatges de programació funcional.

A més a més, la noció de reconeixement de patrons estén de manera natural el procés de dades XML amb l'ajuda de seqüències de patrons ignorant el que hi ha a la dreta (ang.:right-ignoring). En aquest context, les comprehensions de seqüències (iteració sobre Generadors-Filtres-Mapeig) són útils per a formular consultes. Aquestes característiques fan de Scala l'ideal per a desenvolupar aplicacions com a serveis web.

Scala té tipus estàtics[modifica | modifica el codi]

Scala està equipat amb un sistema de tipus expressiu que assegura estàticament que les abstraccions s'utilitzin de manera coherent i segura. En particular el sistema de tipus suporta:

  • classes genèriques,
  • anotacions de variació,
  • acotacions de tipus per dalt i per baix,
  • classes internes i tipus abstractes com a membres de l'objecte,
  • tipus compostos,
  • auto referències tipificades explícitament,
  • vistes, i
  • mètodes polimòrfics.

Un mecanisme d'inferència de tipus permet que l'usuari no hagi d'anotar els tipus de manera redundant. En combinació, aquestes característiques proporcionen una base potent per a la reutilització segura de les abstraccions de programació i també per a l'extensió de programari amb tipus de dades segurs.

Scala és extensible[modifica | modifica el codi]

El disseny de Scala reconeix el fet que, a la pràctica, el desenvolupament d'aplicacions d'un domini específic requereix extensions de llenguatge igualment específiques. Scala proporciona una combinació única de mecanismes de llenguatge que fan fàcil afegir noves construccions al llenguatge, amb suavitat, en forma de biblioteques:

  • qualsevol mètode pot ser emprat com a operador infix o postfix, i
  • els tancaments es construeixen automàticament depenent del tipus esperat.

Un ús conjunt d'ambdues característiques facilita la definició de noves instruccions sense estendre la sintaxi i sense emprar tècniques de meta-programació com ara macros.

Scala interopera amb Java i .NET[modifica | modifica el codi]

Scala està dissenyat per a interoperar bé amb entorns populars de programació com Java 2 (JRE) i el marc .NET (CLR). En particular, la interacció amb llenguatges orientats a objectes de gran acceptació com Java i C# és força suau. Scala té el mateix model de compilació (compilació separada, càrrega dinàmica de classes) que Java i C#, permetent accedir a milers de biblioteques d'alta qualitat.

Exemple: Hola món[modifica | modifica el codi]

Per començar:

Al Linux el podeu instal·lar pels paquets de la distribució. Altrament el compilador es pot descarregar d'aquí[1] o com endollable en un entorn integrat de desenvolupament aquí[2]

El programa Hola món en Scala:

 // fitxer src/test/hola-mon.scala
 package test

 object Principal {
    def main(args: Array[String]) = Console.println("Hola món!")
 }

alternativa derivant l'objecte Application[3] (obsolet des de 2.9, ang:deprecated, eliminat a 2.11) es recomana fer servir el mòdul App

 // fitxer src/test/hola-mon.scala
 package test

 object Principal extends Application { // Application defineix main
    // la inicialització del mòdul executa les expressions del cos.
    Console.println("Hola món!") 
 }

El mòdul App afegeix a Application la propietat args que permet accedir als arguments.[4]

 // fitxer src/test/hola-mon.scala
 package test

 object Principal extends App { 
    
    Console.println ("Hola món!, els arguments són:" ++ args.mkString (", "))

 }
  • Compilació i execució a consola de comandes:[5]
mkdir classes   # subdirectori per al codi objecte

# compilació (-cp és classpath, -d és directori per a la sortida) 
scalac -cp classes -d classes src/test/hola-mon.scala

# execució
scala -cp classes test.Principal          # execució pel nom de l'objecte que conté el mètode ''main''

Hola món!

Gestor de projectes SBT[modifica | modifica el codi]

Aquesta manera de treballar directament amb el compilador ha quedat una mica antiquada. Si ho fem a través del gestor de projectes SBT,[6] aquest ens descarregarà automàticament la versió sol·licitada del compilador i de les dependències del projecte, esmentades al fitxer de projecte.[7]

Caldrà però seguir l'esquema de directoris especificat per l'SBT.[8]

Al Linux el trobarem al gestor de paquets o bé el podem descarregar d'origen[6] si volem la darrera versió.

A l'avaluador d'expressions (bucle Read-Eval-Print)[modifica | modifica el codi]

scala

scala> println( "Hola món")
Hola món

scala>

Amb el gestor de projectes SBT podem engegar l'avaluador REPL amb la comanda següent:

// fitxer de projecte build.sbt
lazy val root = (project in file(".")).
  settings(
    name := "hola_món",
    version := "1.0",
    scalaVersion := "2.11.7"
  )
sbt consoleQuick    // engega REPL (Read-Eval-Print Loop) sense carregar les classes del directori i subdirectoris
sbt console         // engega REPL carregant les classes del directori i subdirectoris

scala>
  • Avaluació d'un mòdul Application (conté mètode main) (obsolet, ang:deprecated)[3]
scala> :reset     // reinicialitza l\'avaluador
scala> :paste
...
scala> Main       // avaluació del mòdul Application
  • Avaluació d'un mòdul App (versió moderna de Application, incorpora membre args).
scala> :reset     // reinicialitza l\'avaluador
scala> :paste
...
scala> Main.main(Array())       // avaluació del mòdul App

Característiques[modifica | modifica el codi]

Degut a ésser un llenguatge dissenyat inicialment per a la màquina virtual JVM, hereta del Java les característiques del lèxic i els tipus bàsics i les seves operacions així com una sintaxi molt similar.

Els punt-i-coma a final de línia es poden ometre (des de la versió 2).

Vegeu l'especificació del llenguatge.[9]

tipus especials[modifica | modifica el codi]

Vegeu Tipus unificats (amb gràfic) a la ref.[10]

Unit
Tipus d'un sol valor que és: ()       // no-op : no fer res (element neutre per l'esquerra de les operacions)

Resultat buit en efectes coŀlaterals (actualitzacions, impressions). Equival al tipus buit (void de Java i C)

Any
generalització d'un tipus qualsevol
AnyVal
generalització dels tipus primaris (els suportats per la CPU) que es passen per valor.
AnyRef
generalització dels tipus que es passen per referència. Correspon a Java.lang.Object
ScalaObject
base per als objectes de Scala (obsolet, retirat a partir de v2.10)[11]
Null
es considera subtipus de qualsevol tipus que es passi per referència (classe o bé object), així el seu valor null pot pertànyer a tots ells.
Nothing
Tipus deshabitat (de valor impossible). És subtipus de tots els tipus. Té diversos usos:[12]
  • És el tipus de retorn de les rutines que mai no retornen res, és a dir, que sempre acaben llançant excepcions.
  • El tipus List[Nothing] és subtipus de totes les llistes (covariants en el tipus de l'elem.) però no pot tenir elements, així el seu únic valor és Nil que és una extensió del tipus List[Nothing]. Això fa de Nil subtipus de List[T] per qualsevol T.[12]
Symbol
literals: identificadors precedits per l'apòstrof.[13]
scala> val saluda = 'hola
saluda: Symbol = 'hola

scala> saluda match {        // salt de carro després de '{' o bé '(' activa el mode multi-línia
     | case 'hola => println( "Benvingut!")         // "|" no és part del codi sinó l'apuntador de l'intèrpret en multi-línia
     | case 'adéu => println( "A reveure!") }       // '}' al mateix nivell, tanca el mode multi-línia
Benvingut!

encaix segons el valor o el tipus[modifica | modifica el codi]

Primer valors, després tipus (de més a menys específic)

package test ;
 
object Main extends App {
 
  val llista: List[Any] = List( true
      , 'c'
      , 8:Byte, 270:Short, 300 /* Int */, 200000000000L
      , 2.5F, 3.12345678 /* Double */
      , "abc") ;
  for (elem <- llista) {
    elem match {
           case 2 | 3 => print( "dos o tres") ;
           case w: AnyVal => w match { // tipus primitius que es passen per valor
                     case v :Char => printf("%c u\\%04x", v, v.toInt );  // caràcter i hexadecimal
                     case v :Byte => printf("és un byte: %d", v) ;
                     case v :Short => printf("és un short: %d", v) ;
                     case v :Int  => printf("és un int: %d 0%o x%08x", v, v, v);     // decimal, octal i hexad.
                     case v :Long => printf("%d", v) ;
                     case v :Float => printf("%.2f", v) ;
                     case v :Double => printf("%.8f", v) ;
                     case v => print( v)
                     }
           case w: AnyRef => w match { // tipus que es passen per referència
                     case v :String => printf("%s", v) ;
                     case v => print( v.toString) ;
                     }    
           }
    }
    println("") 
  }
}

dóna

true
c u\0063
és un byte: 8
és un short: 270
és un int: 300 0454 x0000012c
200000000000
2,50
3,12345678
abc

Igualtat estructural vs. referencial[modifica | modifica el codi]

En la comparació d'objectes, l'operador (==) no correspon en Java al (==) sinó al mètode equals. [14]

Scala Java Comentaris
igualtat de les referències eq, ne
(classe AnyRef)[15]
(==), (!=)
igualtat estructural (==), (!=)
(classe Any)[16]
.equals() excepte per al tipus Array[T][14]
igualtat de continguts .sameElements(that: GenIterable[A])
(classe GenIterable)[17]

A Scala la igualtat d'objectes que es passen per referència AnyRef[18] equival a

x == y <=> if (x eq null) (y eq null) else x.equals(y)

polimorfisme paramètric[modifica | modifica el codi]

A més del polimorfisme per especialització dels objectes, les funcions es poden parametritzar per Tipus

 def nom_funció[VariableDeTipus](param1: VariableDeTipus, ..) = ...

exemple:

object Main extends App {
  def imprimeix[T]( dada: T) = 
    dada match {
      case v: String => println( v) ;
      case v => println (v.toString) ;
    }
  imprimeix[Int]( 5) ;
  imprimeix[String]( "abc") ;
  imprimeix( 2);              // si no s'explicita el tipus, es dedueix dels paràmetres actuals.
  imprimeix( "def") ;
}

àlies de tipus[modifica | modifica el codi]

type LaMevaFun[A,B,C] = B => A => Pair[B, C]

Relacions de Subtipus, Covariança, Contravariança[modifica | modifica el codi]

Vegeu ref.[19][20]

  • A <: B indica A subtipus de B
  • A >: B indica B subtipus de A
  • Una funció f: (A' => B') és subtipus d'una altra funció g: (A => B) (f pot ser emprada en el lloc de g) si el resultat és de tipus més especialitzat (B' <: B) i admet arguments de tipus més general (A' >: A).[21]

Es diu que la funció f subtipus de g és covariant en el resultat (la relació varia en el mateix sentit) i contravariant en el paràmetre de valor. El tipus funció definit a Predef és

// el prefix + en un paràmetre de tipus indica covariança
// el prefix - en un paràmetre de tipus indica contravariança

type Function[-A, +B] = (A)  B          // del mòdul predefinit Predef

Perquè una classe C sigui subtipus d'una altra D, C ha de contenir subtipus de tots els mètodes de D, a banda d'altres requeriments.[22]

L'efecte buscat és poder assegurar la covariança de la classe d'una col·lecció respecte del tipus de l'element (exemple: A <: BList[A] <: List[B]), o bé la contravariança:

covariança
C[A] <: C[B] sempre que A <: B
contravariança
C[A] >: C[B] sempre que A <: B.

Perquè una classe C[T] sigui covariant en un tipus T, C[+T], aquest tipus només pot aparèixer en posicions covariants (resultats) dels mètodes i no en les contravariants (arguments).[19][20] Al revés per a les classes contravariants en T: C[-T]. Però sembla que és una condició necessària però no suficient.[22]

Per utilitzar un tipus covariant en posicions d'argument (contravariant) en un mètode i continuar mantenint la covariança del mètode i, per tant, de la classe, cal fer servir el truc del límit inferior.[20] Cal assegurar que l'argument ha d'admetre tipus més generals que T, fent servir per a l'argument un tipus existencial: aquells tipus U tals que U >: T.

class ListNode[+T](h: T, t: ListNode[T]) {
  def head: T = h                               
  def tail: ListNode[T] = t

  // per poder utilitzar el tipus T en l'argument i mantenir la covariança de la classe respecte a T
  // l'argument (posició contravariant) ha d'admetre qualsevol supertipus U >: T
  def prepend[U >: T](elem: U): ListNode[U] =   
    ListNode(elem, this)                        
}

restriccions en els tipus[modifica | modifica el codi]

Els tipus poden parametritzar funcions, classes, objectes i trets. Admeten a més restriccions especials.

En el següent exemple Tipus ha d'ésser subtipus de TCotaSuperior.[23]

 type Tipus <: TCotaSuperior  // 'Tipus' ha d'ésser subtipus (més especialitzat) de 'TCotaSuperior'

en el següent exemple Tipus ha d'ésser superclasse de TCotaInferior.[24]

 type Tipus >: TCotaInferior  // 'Tipus' ha d'ésser supertipus (més general) de 'TCotaInferior'

Tipus producte[modifica | modifica el codi]

Tuples (tipus producte anònim)[modifica | modifica el codi]

Definides les classes Tuple2, Tuple3, .., Tuple22.
Pair equival a Tuple2, Triple a Tuple3
object Main extends App {

  val parell = new Pair( 10, "abc") ;

  val parell2 = Pair( 10, "abc"); // alternativa amb el mètode ''fàbrica d'instàncies'' 
                                   // (ang: ''factory method'') a l'objecte Pair.

  val Pair(primer, segon) = parell;  // encaix de la tupla

  println( primer) ;
  println( segon) ;
}

Classes (tipus producte amb nom), i Objectes singulars[modifica | modifica el codi]

A Scala se separen les parts dinàmica i estàtica de les classes del Java en class i object, objecte singular (únic) que conté els membres estàtics.

Extensibilitat. Els object tenen un tractament unificat amb les classes respecte a l'extensibilitat com si fossin classes d'una sola instància, que els permet implementar interfícies i estendre-les amb altres object i traits com a l'exemple amb l'objecte App.

Grafia. Els identificadors dels mòduls object comencen per majúscula segons el llibre d'estil.

La inicialització dels object i també de les classes, es produeix seqüencialment processant definicions i instruccions executables, com a l'ML Estàndard i l'OCaml.

// ''App'' és l'object d'engegada que conté el mètode ''main''

object ProvaFiltratge extends App {

  def filtrar(xs: List[Int], llindar: Int) = {

    def processa( ys: List[Int]): List[Int] =    // funció a l'interior d'una altra funció
      if (ys.isEmpty) ys
      else if (ys.head >= llindar) ys.head :: processa( ys.tail)
      else processa( ys.tail)

    processa( xs)
  }
  println( filtrar( List(1, 9, 2, 8, 3, 7, 4), 5))
}

En importar una classe de Java, l'identificador designa la classe equivalent en Scala amb els mètodes dinàmics (de la instància), així com l'Object que conté els mètodes, variables i constants estàtics.

import java.lang.{Color}
val vermell = new Color( 0xFF, 0, 0);                    // constructor de la ''classe''
val componentRoigDelBlanc = Color.getRed( Color.WHITE);  // membres estàtics de l{{'}}''Object''
els object (mòduls amb els membres estàtics) de les col·leccions incorporen generadors
object List {
  def apply[T] (elements: T*)  // genera una llista amb els elements proporcionats com a paràmetres
                               // ''apply'' és el nom de funció per omissió (es pot ometre)
                               // així List (1, 2, 3)  equival a List.apply(1, 2, 3)

  def empty[T] ()              // generador de col·lecció buida
}
override
els mètodes que es redefineixen han de dur el qualificatiu override per distingir-los dels que es volen nous, per evitar que si algú afegeix un mètode amb el mateix nom i signatura a alguna de les superclasses o trets que s'hereten el comportament esdevingui diferent del que se n'espera.
val (al paràmetre de la classe)
converteix el paràmetre en un camp membre de la classe, evita redefinir-lo i haver d'assignar-li el valor del paràmetre dins el constructor.
  class Classe( val p1: Tipus, p2: Tipus2 = vPerDefecte, ...)  // amb val, p1 passa a ser membre de la classe

Extractors[modifica | modifica el codi]

Un extractor és un objecte singular que implementa la funció unapply que permet extreure'n el valor en encaixar patrons en les opcions de les clàusules match.[25]

    // el següent codi genera un objecte extractor
    object Doble {
      def apply (x: Int): Int = x * 2                                           // generador
      def unapply (z: Int): Option[Int] = if (z % 2 == 0) Some (z/2) else None  // invers del generador
    }

    object ProvaDoble extends App {
      val x = Doble (21)             // aplica 'apply' (nom de funció per omissió a les expressions)
      x match { case Doble (n) => Console.println(n) } // aplica 'unapply' (nom de funció per omissió als patrons)
    }

tipus "suma" amb classes encaixables[modifica | modifica el codi]

Les classes encaixables (ang:case class) permeten definir com a subtipus les variants d'un tipus suma (unió discriminada).

Les classes encaixables són classes per a les quals es defineix, automàticament, un objecte extractor (secció precedent), facilitant l'ús del nom de la classe i els seus paràmetres com a patró en un selector per encaix de patrons match/case.

abstract class Arbre[T]
// ''val'' al paràmetre el declara membre de la classe, estalvia redeclaracions
case class Fulla[T]( val dada: T) extends Arbre[T] 
case class Branca[T]( val dada: T, val esquerra: Arbre[T], val dreta: Arbre[T]) extends Arbre[T]

object Main extends App { 

  def imprimeixArbre[T]( arbre: Arbre[T]):Unit = 
    arbre match {
            case Fulla( d) => println ("fulla: " ++ d.toString)
            case Branca( d, esq, dreta) => { imprimeixArbre( esq) ;
                                             println("branca: " ++ d.toString) ;
                                             imprimeixArbre( dreta) ;
                                           }
    }

  // els constructors de case classes no necessiten ''new'', de fet no són constructors sinó generadors membres de l'objecte extractor
  // l'expressió {Fulla( 1)} equival a {Fulla.apply( 1)}  // ''apply'' és el nom de funció per omissió.

  val arbre1 = Fulla( 1);   // si s'omet el paràmetre de tipus ''[T]'', es pren el tipus del paràmetre.   
  val arbre2 = Branca[Int]( 2, arbre1, arbre1) ;
  imprimeixArbre( arbre2) ;
}

Equivalència en ML Estàndard:

datatype 't Arbre = Fulla of 't | Branca of ('t * 't Arbre * 't Arbre)

Equivalència en Haskell:

data Arbre t = Fulla t | Branca t (Arbre t) (Arbre t)

Vegeu exemple #Encaixos de patrons.

exhaustivitat als encaixos (atribut sealed)[modifica | modifica el codi]

L'atribut sealed (segellat) a la classe abstracta de la qual es deriven les variants encaixables (case classes), permet circumscriure'n la definició de variants al fitxer on està definida, formant un conjunt tancat, facilitant que el compilador pugui avisar per manca d'exhaustivitat en els encaixos.

Exemple de compilació amagant el cas Fulla:

sealed abstract class Arbre[T]              // 'sealed': classe segellada, per al control d'exhaustivitat als encaixos
case class Fulla[T]( val dada: T) extends Arbre[T] 
case class Branca[T]( val dada: T, val esquerra: Arbre[T], val dreta: Arbre[T]) extends Arbre[T]

object Main extends App { 

  def imprimeixArbre[T]( arbre: Arbre[T]):Unit = 
    arbre match {
            // case Fulla( d) => println ("fulla: " ++ d.toString)    // comentat expressament per veure l'avís del compilador
            case Branca( d, esq, dreta) => { imprimeixArbre( esq) ;
                                             println("branca: " ++ d.toString) ;
                                             imprimeixArbre( dreta) ;
                                           }
...
sbt compile
[info] Compiling 1 Scala source to ...
[warn] /home/gabi64/lleng/scala/sealed/Main.scala:9: match may not be exhaustive.
[warn] It would fail on the following input: Fulla(_)
[warn]     arbre match {
[warn]     ^
[warn] one warning found
[success] Total time: 10 s, completed 07/06/2016 15:35:50

Seqüències numèriques[modifica | modifica el codi]

  • Range // classe de seqüències de Int
  • NumericRange[T: Integral] // classe de seqüències de tipus que implementen Integral[T] (anell unitari íntegre ordenat euclidià: operacions dels enters)

Les següents expressions retornen un iterador, que es pot fer servir com a generador en una clàusula for, o bé aplicar toList per obtenir-ne una llista.

(1 until 5)  // interval obert (exclou límit superior)
(1 to 5)     // interval tancat (inclusiu)
(1 to 5 by 2)  // 'by' precedeix l'increment

Llistes[modifica | modifica el codi]

object Main extends App {
  val llista = List( 1, 2, 3) ;
  def descriuLlista[T]( llista: List[T]) = 
    llista match {
	     case Nil => println ("llista buida") ;
        
	     case ll @ _::_ =>       // as-pattern (variable @ patró): unifica variable i terme encaixat
                          println ("llista no buida: " + ll.toString) ;
    }
  descriuLlista( llista )
}

Llistes per comprensió[modifica | modifica el codi]

La clàusula for admet una primera part amb generadors i filtres (clàusula if), i una segona part de mapeig que pot ésser o bé un bloc d'efectes col·laterals, o bé una clàusula yield (trad.:produeix) que combina el resultat de l'expressió o bloc en una col·lecció del tipus del primer generador.

 // retorna els dobles dels parells de l'interval
 // en una col·lecció del tipus del primer generador
 
 val iterador = for (i <- Iterator.range( des_de, topall_exclòs) 
                     if i % 2 == 0
                     ) yield 2 * i   // el tipus resultant és Iterator[Int]
 println (iterador.toList)

 // equivalent amb rangs: (des_de to topall_inclòs)
 val iterador = (1 to 12).iterator.filter(_ % 2 == 0).map(_ * 2) 
 println (iterador.toList)
Encadenament monàdic[modifica | modifica el codi]

Les llistes per comprensió introdueixen l'encadenament monàdic: encadenament d'una acció amb una funció d'efectes col·laterals sobre el resultat precedent, el tipus final és el de l'acció primera amb resultat determinat per la funció (avaluada en segon lloc). L'acció també és assimilable a l'avaluació d'una col·lecció amb el tipus de l'element com a tipus del resultat.

La clàusula for incorpora una seqüència de generadors, guardes (clàusula if), expressions basades en resultats precedents (clàusula let) i cos, que es tradueix en l'ús de les primitives flatMap, filter i finalment map per al cos, retornant sempre una col·lecció del tipus del primer generador, amb elements del tipus del resultat del cos.

scala> val opt3 = Some(3)
scala> val opt4 = Some(4)

scala> for { x <- opt3; 
             y <- opt4 } yield (x+y)
res4: Option[Int] = Some(7)

// desplegament monàdic equivalent

scala> opt3 flatMap { x => opt4 map { y => x + y}}
res5: Option[Int] = Some(7)

Per als tipus amb "efecte fallada" (element absorbent per a flatMap), com ara Option[T], Try[T], List[T], ..., l'encadenament de generadors, filtres i expressions evita la necessitat de consultar la correcció del resultat a cada pas, doncs l'encadenament progressa només per als elements no absorbents per l'esquerra respecte a l'encadenament (aquí l'op. flatMap).

-- ús de l'encadenament monàdic per al tipus Try[T] (variants Success[T] i Failure[Throwable])
-- Try encapsula, a l'exemple, l'avaluació de l'operació de lectura, retornant Failure(err) en cas d'excepció
-- Failure(..) és l'element absorbent per l'esquerra de ''flatMap'' per al tipus Try, fent que l'encadenament no progressi.

val result = for (dividend <- Try(Console.readLine("entreu dividend: ") ;
                  divisor  <- Try(Console.readLine("entreu divisor: ")
             ) yield dividend / divisor
result match {
      case Success(v) => println ("correcte: " + v.toString)
      case Failure(err) => println ("pifiada: " + err.getMessage)
      }

Vegeu presentació "Introduction to Monads in Scala".[26]

Variables i mètodes[modifica | modifica el codi]

El tipus de les declaracions es pot ometre si es dedueix de la seva inicialització de manera inequívoca, altrament el compilador avisarà.

val identificador :Tipus = expressió     // amb val: assigna el valor de l'expressió com a constant
var identificador :Tipus = expressió     // amb var: variable mudable
def identificador :Tipus = expressió     // amb def: es recalcula a cada invocació

// Les inicialitzacions de variables amb el comodí '_' 
// assignen l'element zero del tipus si és subtipus de 'AnyVal' (pas per valor) 
// o bé 'null' si és subtipus de 'AnyRef' (pas per referència)

scala> var v : Boolean = _
v: Boolean = false

scala> var v : Int = _
v: Int = 0

scala> var v : String = _
v: String = null

La diferència entre una definició amb val i un mètode (definit amb def) és que l'assignació amb val es calcula una única vegada, mentre que amb def es recalcula a cada invocació.

Operadors[modifica | modifica el codi]

Els operadors són mètodes definits amb símbols.

class Q {
          def ##(that:Q) = this != that
        }

La precedència la determina el primer caràcter del símbol. (Vegeu apartat 'Infix operators' al manual de ref. del llenguatge).

(all letters)
|
^
&
< >
= !
:
+ -
* / %
(all other special characters)

associativitat per la dreta explícita[modifica | modifica el codi]

L'associativitat la determina el darrer caràcter del símbol, que si és ':' llavors és associatiu per la dreta, altrament associatiu per l'esquerra. Vegeu ScalaReference.pdf (paràgraf 6.12.3 Infix Operations).

L'associativitat per la dreta, en agrupar per la dreta, requereix que l'operador en notació infix s'avaluï com a mètode del tipus de l'operand de la dreta:

// si 'x1' i 'x2' són elements i 'xs' és una col·lecció
x1 +: x2 +: xs  x1 +: (x2 +: xs) // (+:) ha de ser mètode del tipus de l'operand de la dreta: la col·lecció

L'associativitat per la dreta pels ':' finals, afecta a més dels operadors, als constructors definits amb símbols, com ara (::) el "Cons" de les llistes i també (#::) el "Cons" dels Stream.

// (++:) concatenació amb associativitat per la dreta (pels ':' final)
// (++:) és membre del tret "Traversable", que haurà d'implementar l'operand de la dreta

// StringBuilder és una ''String'' mudable que implementa ''Growable'' (que pot créixer).
// seq.mkString converteix una seqüència en una String

val strBldr = "abc" ++: "def" ++: new StringBuilder( "ghi")

val novaStr = strBldr.mkString

Trets (ang:traits): el sistema d'extensió de classes i d'objectes singulars[modifica | modifica el codi]

La paraula anglesa trait es pot traduir per tret o característica (vegeu diccionaris d'anglès).

Un tret encapsula una característica incorporable a una classe d'objectes o bé a un object singular.

Els trets poden contenir estructura (camps), comportament (mètodes) i tipus associats, tant abstractes com concrets (amb implementació), però no són instanciables per si mateixos, ni podien tenir paràmetres de valor. Això canviarà pròximament permetent els paràmetres als trets.[27]

Són assimilables als interface del Java, però amb el potencial d'una classe abstracta.

import java.awt.{Color} ;

trait Acoloriment {
	var color:Color; 
	def pinta( nouColor: Color): Unit = { color = nouColor}
	def imprimeixColor: Unit = { print( "color: "); println( color) } 
}

trait Cloneable extends java.lang.Cloneable {
  override def clone(): Cloneable = { super.clone(); this }
}

trait Resetable {
  def reset: Unit
}

def cloneAndReset(obj: Cloneable with Resetable): Cloneable = {
  val cloned = obj.clone()
  obj.reset
  cloned
}

herència múltiple[modifica | modifica el codi]

L'herència múltiple es basa en la incorporació de trets.

import java.awt.{Color} ;

class Punt(xInicial:Int) {
	var x: Double = xInicial.toDouble ;
	def desplaça( dx: Double): Unit = { x = x + dx}
	def imprimeixPosició: Unit = { print( "posició: "); println( x) } 
}

trait Acoloriment {
	var color: Color; 
	def pinta( nouColor: Color): Unit = { color = nouColor}
	def imprimeixColor: Unit = { print( "color: "); println( color) } 
}

class PuntAcolorit( xInicial:Int, colorInicial: Color) extends Punt( xInicial) with Acoloriment {

	var color = colorInicial ;

	def desplaçaIPinta( dx: Double, nouColor: Color) = { 

	  super[Punt].desplaça(dx) ;
	  super[Acoloriment].pinta(nouColor) ;
	}
}

object Main extends App {
	
  val punt = new PuntAcolorit( 0, Color.WHITE) {   // extensió en instanciar
                                      // només aquesta instància incorpora l'extensió
	  
	  def desplaçaRàpid( dx: Double) = { 
	 	  x = x + 10.0 * dx
	  }
  }
  punt.desplaçaIPinta(2, Color.BLUE) ;
  punt.desplaçaRàpid( 3.5)
  punt.imprimeixPosició ;
  punt.imprimeixColor ;
}

dóna el resultat:

posició: 37.0
color: java.awt.Color[r=0,g=0,b=255]

Restricció del tipus propi (ang:self-types)[modifica | modifica el codi]

La restricció al tipus propi en un tret (especificant un identificador, com ara self, abans de =>) permet relacionar un tret amb una instància concreta d'un tipus abstracte.[28] És possible utilitzar this per aquest afer, però en cas de referir-s'hi en una classe interna (niuada) l'identificador this quedaria tapat. this es referiria a l'objecte corresponent a la classe interna i no a l'externa.

Del document "Scala Component Abstractions" de M.Odersky.[29]

abstract class Graf {
  type Node <: NodeBase;  // volem que els nodes implementin NodeBase

  class Arc(val origen: Node, val destí: Node) {}

  class NodeBase {
    def connectaAmb(n: Node): Arc =
      new Arc(this, n);   // iŀlegal !! 'this' no és de tipus Node
  }
}

Solució amb self-types

abstract class Graf {
  type Node <: NodeBase;  // volem que els nodes implementin NodeBase

  class Arc(val origen: Node, val destí: Node) {}

  abstract class NodeBase {
    self: Node =>  // requeriment que el tipus propi sigui subtipus de Node en obtenir-ne una classe concreta
    def connectaAmb(n: Node): Arc =
      new Arc(self, n);   // ara si que passa la compilació
  }
}

// classe concreta de Graf que incorpora la classe concreta Node
class GrafEtiquetat extends Graf {
  class Node(etiq: String) extends NodeBase {
    def getEtiqueta: String = etiq;
  }
}

Refinament de tipus (ang: Downcasting)[modifica | modifica el codi]

Caracterització al tipus d'una classe derivada, amb el mètode asInstanceOf prèvia comprovació amb isInstanceOf.

def fun( param: Tipus) = if (param.isInstanceOf[Subtipus]) { 
                               val subtipParam = param.asInstanceOf[Subtipus]; 
                               ... }
                         else ...

Enumeracions[modifica | modifica el codi]

object Main extends App {

    object DiaSetm extends Enumeration {
      type DiaSetm = Value
      val Dl, Dt, Dc, Dj, Dv, Ds, Dg = Value
    }
    import DiaSetm._

    def ésDiaFeiner(d: DiaSetm) = ! (d == Ds || d == Dg)

    println (DiaSetm.values filter (ésDiaFeiner) mkString (", "))
  }
$ sbt run
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256M; support was removed in 8.0
[info] Set current project to enums (in build file:/home/gabi64/lleng/scala/enums/)
[info] Compiling 1 Scala source to /home/gabi64/lleng/scala/enums/target/scala-2.10/classes...
[info] Running Main 
Dl, Dt, Dc, Dj, Dv
[success]

Parametrització de tipus amb cota superior[modifica | modifica el codi]

Admetre tipus més especialitzats que un tipus determinat.

trait Similar {
  def isSimilar(x: Any): Boolean
}

case class MyInt(x: Int) extends Similar {
  def isSimilar(m: Any): Boolean =
    m.isInstanceOf[MyInt] &&
    m.asInstanceOf[MyInt].x == x
}

object UpperBoundTest extends App {

  def findSimilar[T <: Similar](e: T, xs: List[T]): Boolean =
    if (xs.isEmpty) false
    else if (e.isSimilar(xs.head)) true
    else findSimilar[T](e, xs.tail)

  val list: List[MyInt] = List(MyInt(1), MyInt(2), MyInt(3))

  println(findSimilar[MyInt](MyInt(4), list))
  println(findSimilar[MyInt](MyInt(2), list))
}

funcions amb llista d'arguments variable[modifica | modifica el codi]

El símbol '*' després del tipus del darrer argument, indica que el paràmetre actual corresponent pot aparèixer zero o més vegades, i que cal prendre aquest argument com una seqüència.

def imprimeix( args: String* ) = {
  for( arg <- args ){
         println( arg );
  }
}
imprimeix( "abc", "def", "ghi")

funcions anònimes com a expressió[modifica | modifica el codi]

exemple:

(x: Int, y:Int) => x + y         // el tipus del resultat és deduït per inferència en aquest cas

En l'exemple següent el compilador dedueix els tipus dels paràmetres de la funció sobre la qual s'aplica.

object Main extends App {
  val llista = List(1,2,3,4);  // pels literals infereix tipus List[Int]

  // aplica la funció a un acumulador i cada valor de la llista
  // els tipus dels paràmetres els dedueix de l'aplicació parcial (funció ometent paràmetres) precedent.
  //     tipus de llista.foldleft( 0) : (Int * Int) => Int

  val result = llista.foldLeft( 0) { (x,y) => x+y }; 

  Console.print("resultat: ") ;
  Console.println( result);
}

dóna l'esperat

resultat: 10

Aplicació parcial d'operadors i mètodes[modifica | modifica el codi]

L'aplicació parcial d'operadors s'expressa substituint un o més operands pel comodí '_' en una expressió. Ex. (3 + _) : (Int => Int)

  • També es pot referir a mètodes: (_.toString) : (Any => String)
scala> :type List[Any](1, 2.5, "abc") .map (_.toString)
List[String]

scala> def floatMes3: (Float => Float) = (_ + 3)    // cal la restricció de tipus
floatMes3: Float => Float

Aplicació parcial d'una funció[modifica | modifica el codi]

Per obtenir el valor de funció dels mètodes def d'un object (que són mètodes estàtics) i poder passar-lo com a paràmetre a una funció d'ordre superior, cal substituir els paràmetres no aplicats amb un únic comodí '_'.

Scala no té polimorfisme d'ordre superior (ang:impredicative polymorphism), per tant les funcions que es passen com a paràmetre no poden ser polimòrfiques (cal donar valor als seus paràmetres de tipus).

// Definint ''flip'' (intercanviar l'ordre d'arguments) per fer-lo servir més tard
scala> def flip[T1, T2, R]( f : T1 => T2 => R) (x: T2) (y: T1) : R = f (y) (x)
flip: [T1, T2, R](f: T1 => (T2 => R))(x: T2)(y: T1)R

scala> def cons[A] (x: A) (xs: List[A])  = x :: xs
cons: [A](x: A)(xs: List[A])List[A]

scala> :type cons[Int] (5) _            // tipus aplicant només un paràmetre
List[Int] => List[Int]
                                                                                                 
scala> :type cons[Int] _                // tipus aplicant zero paràmetres
Int => (List[Int] => List[Int]) 

scala> flip (cons[Int] _) (List(2,3)) (1)    // passant 'cons' a 'flip' (funció d'ordre superior)
res0: List[Int] = List(1, 2, 3)

Composició i currificació de funcions[modifica | modifica el codi]

La interfície Function1 de funcions d'un argument aporta mètodes per la composició de funcions.[30]

La interfície Function2 de funcions de dos arguments aporta mètodes per obtenir versions currificades (.curried) i la inversa (.tupled).[31]

Function3 a Function19 també definides.

package test

object Main extends App {

    // composició de les funcions d'un arg.

    def doble( x: Int) = x * 2
    def suma3( x: Int) = x + 3

    // f compose g == f. g
    // f andThen g == g. f

    // cal especificar '_' per obtenir l'aplicació parcial
    def doblaiSuma3 = (doble _).andThen(suma3 _)   // composició d'esquerra a dreta
    def doblaiSuma3 = (suma3 _).compose(doble _)   // composició amb estil matemàtic, resultat idèntic a l'anterior

    val prova = doblaiSuma3( 5)

    // currificació i aplicabilitat als arg. en tupla

    def cons[A] (x: A, xs: List[A])  = x :: xs
    
    def f[T] = (cons[T] _)  // cal especificar '_' per obtenir l'aplicació parcial

    val x1 = f (1, List(2,3)) 

    // currificació (amb 'curried'' la funció s'aplica als param. separats
    val x2 = f[Int].curried (1) (List(2,3))   // cal explicitar el paràmetre actual de tipus 
}

funcions d'ordre superior[modifica | modifica el codi]

Amb funcions als paràmetres o al resultat.

// en aquest cas, funció que pren una funció d'argument i en retorna una altra

def sum(f: Int => Int): (Int, Int) => Int = {

  def sumF(a: Int, b: Int): Int =             // suma f(x), x <- [a..b]
    if (a > b) 0 else f(a) + sumF(a + 1, b)

  &sumF
}

def sumaDobles = &sum( x => 2 * x)
def sumaQuadrats = &sum( x => x * x)

Definició per patrons d'una funció - El tret PartialFunction[modifica | modifica el codi]

Vegeu "Pattern-matching anonymous functions".[32]

def or : (Boolean, Boolean) => Boolean = 
          { case (true, _) => true; 
            case (false, x) => x
          }

Una definició per seqüència de patrons, si no són exhaustius, és una funció definida parcialment.

Les funcions amb definició per patrons implementen el tret PartialFunction[A, B],[33] amb mètodes molt interessants.

val pf: PartialFunction[A, B] = { case patró1 if condició => ...; 
                                  case patró2 => ... ;
                                  ... }

La funció pf.isDefinedAt(x:A) retorna si el paràmetre encaixa en algun dels patrons !!

El mètode lift com a pf.lift embolcalla el resultat en un Option oferint una versió total de la funció parcial.

El mètode applyOrElse permet especificar una funció per defecte per als casos no definits.

pf.applyOrElse(x, funcPerDefecte)  if(pf isDefinedAt x) pf(x) else funcPerDefecte(x)

El mètode orElse admet la composició amb una altra funció parcial, que s'avalua per als casos no definits de la primera.

scala> val dobleDeParell: PartialFunction[Int, Int] = {
     |   case x if x % 2 == 0 => 2 * x
     | }
dobleDeParell: PartialFunction[Int,Int] = <function1>

scala> dobleDeParell.isDefinedAt(3)
res0: Boolean = false

scala> dobleDeParell.isDefinedAt(2)
res1: Boolean = true

scala> dobleDeParell.lift(3)
res2: Option[Int] = None
  • A les col·leccions:

collect mapeja amb una funció parcial obtenint la col·lecció de les imatges d'aquells elements per als quals la funció està definida:

// interfície GenTraversable
def collect[B](pf: PartialFunction[A, B]): Repr[B]       

// interfície GenTraversableOnce
def collectFirst[B](pf: PartialFunction[A, B]): Option[B]  // obté, si existeix, la primera de les imatges de mapejar amb una funció parcial

Excepcions[modifica | modifica el codi]

Excepcions síncrones. Èxit i Fallada de l'avaluació. La classe Try[modifica | modifica el codi]

El generador Try, de l'objecte estàtic del mateix nom, avalua una expressió llançadora d'excepcions oferint com a resultat dues variants d'encaix (case classes) de la classe Try[T] que són Success(resultat:T) i Failure( excepció:Throwable).

Permet el tractament de les excepcions síncrones.

A més implementa la classe de tipus Mònada amb (Failure(e:Throwable)) com a element absorbent de l'encadenament, de manera que en una clàusula for (avaluació monàdica) la fallada fa que no s'avaluïn les clàusules subsegüents del for evitant la necessitat de comprovació de l'èxit a cada pas.

A l'exemple el tipus del for és com sempre el del primer generador, en aquest cas un tipus Try:

val res = for (dividend <- Try(Console.readLine("entreu dividend: ") ;
               divisor <- Try(Console.readLine("entreu divisor: ")
          ) yield dividend / divisor
res match {
      case Success(v) => println ("correcte: " + v.toString)
      case Failure(err) => println ("error de lectura (del readLine) " + err.getMessage)
      }

Excepcions en la Gestió de recursos[modifica | modifica el codi]

La clàusula try {..} catch (err:Throwable) {..} finally {..} possiblita la gestió d'excepcions i alliberament de recursos.

L'objecte Exception[34] aporta diverses possibilitats addicionals.

gestió automàtica[modifica | modifica el codi]

La recent construcció a Java 7 try-with-resources[35] que encapsula el recurs i el seu alliberament ha fet repensar una clàusula semblant a Scala. Per a l'anomenat "Loan pattern" o patró del préstec (perquè el recurs s'ha de tornar),[36] es proposa la següent construcció (Closeable[37] és una interfície de Java)

def using[A, R <: Closeable](r : R)(f : R => A) : A = {
    var result : A = _
    try {
        result = f(r)
    } finally {
        try {r.close()} 
        catch {case e: IOException => ()}
    }
    result
  }

La biblioteca scala-arm[38] proporciona una implementació a través de la implementació monàdica de les llistes per comprensió, que evita el niuament de construccions en cas d'adquisició de múltiples recursos.

import resource._

// dos recursos, còpia de input a output, amb alliberament automàtic

for( input <- managed(new java.io.FileInputStream("test.txt"); 
     output <- managed(new java.io.FileOutputStream("test2.txt")) {

  val buffer = new Array[Byte](512)
  def read(): Unit = input.read(buffer) match {
    case -1 => ()
    case  n => output.write(buffer,0,n); read()
  }
  read()
}

Assercions[modifica | modifica el codi]

Vegeu Assercions[39] i Opció de compilació -enableassertions[5]

assert( predicat [,missatge]) 
especifica un predicat que cal provar i dispara l'excepció java.lang.AssertionError: assertion failed: missatge
assume( axioma [,missatge]) 
especifica un axioma (asserció que les eines de comprovació estàtica de tipus no han de deduir de proposicions precedents). En temps d'execució actua com un assert disparant l'excepció java.lang.AssertionError: assumption failed: missatge
error( missatge)
per al cas d'estats inconsistents o bé de funció no definida per un cas dels paràmetres
 ???
indica implementació pendent, dispara UnsupportedOperationException("not implemented").[40]
def mètode : Tipus = ???   // mètode pendent d'implementar (per poder passar la compilació)

Precondició[modifica | modifica el codi]

  • la clàusula require ha estat declarada obsoleta i retirada a la v2.8.[41]
  • es recomana l'ús de la definició per patrons que implementa el tret PartialFunction.

Postcondició[modifica | modifica el codi]

{acció} ensuring ( _ => condició [,missatge]) 
asserció com a funció sobre el resultat d'un bloc de codi.
// malament a posta, per fer petar la postcondició
scala> def incrementa( x: Int) = { x - 1 } ensuring (_ > x, "el resultat no és superior")
incr: (x: Int)Int

scala> incrementa (5)
java.lang.AssertionError: assertion failed: el resultat no és superior
  at scala.Predef$Ensuring$.ensuring$extension3(Predef.scala:256)
  at .incr(<console>:10)
  ... 43 elided

XML al codi[modifica | modifica el codi]

Admet notació de llenguatge de marques XML com a literal, amb interpolació d'expressions entre claus "{}". Vegeu[42]

object Main extends App {
  val idioma = "ca" ;
  val títol = "El meu títol" ;
  val xml_títol = <title> {títol} </title>;    // entre claus '{' <expr> '}' interpolació d'expressions.
  val xml_capsalera = <head> {xml_títol} </head>

  // substitució a l'atribut, hi afegeix les cometes.
  val xml_pàgina = <html lang={idioma}>  
                     {xml_capsalera} 
                     <body> Aquesta pàgina duu per títol: {títol} </body>
                   </html> ;
  val capsal_xml ="<?xml version='1.0' encoding='UTF-8' ?>\n" ;
  val doctype = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" " +
           " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" ;
  println( capsal_xml + doctype + xml_pàgina) ;
}

El paquet de tractament de XML scala.xml, que anteriorment formava part de la biblioteca base de Scala, ha estat separat en un paquet Jar independent que es pot obtenir amb el codi font al GitHub d'aquí. L'API és aquí.

Implícits[modifica | modifica el codi]

Scala té diversos mecanismes relacionats amb el tipus que busquen automàticament la implementació disponible (visible en l'àmbit d'ús) d'una construcció genèrica (parametritzada pel tipus) que més s'ajusta al tipus emprat (tipus actual).

Vistes de tipus (conversions implícites)[modifica | modifica el codi]

Si el tipus del paràmetre actual no correspon amb el del paràmetre formal, però existeix una funció de conversió implícita entre ambdós tipus:

// del Predef
// char2int permet emprar valors de tipus Char en paràmetres Int

implicit def char2int (x: Char): Int

Restricció de Convertibilitat (View bound)[modifica | modifica el codi]

És una alternativa menys estricta que un requeriment de subtipatge.

type A <% T // indica que cal que existeixi una conversió implícita de A a T. Vegeu Scala Views[43]

trait Set[A <% Ordered[A]] ...  // consulteu la ref.

Instàncies implícites de classes de tipus[modifica | modifica el codi]

El mecanisme dels implicit objects permet obtenir la instància (implementació) d'una classe de tipus (classe abstracta genèrica parametritzada per un tipus) per al tipus del paràmetre actual.

A l'exemple el paràmetre (implicit m: Monoid[A]), m obtindrà el valor de la instància específica del genèric Monoid[A] per al tipus del paràmetre actual emprat en la crida a la funció def sumaLlista[A](xs: List[A])(implicit m: Monoid[A]).

    abstract class SemiGrup[A] {
      def suma(x: A, y: A): A
    }

    abstract class Monoid[A] extends SemiGrup[A] {
      def zero: A
    }
    object ImplicitTest extends App {

      // mòdul instància de Monoid per al tipus String

      implicit object StringMonoid extends Monoid[String] {
        def suma( x: String, y: String): String = x concat y
        def zero: String = ""
      }

      // mòdul instància de Monoid per al tipus Int

      implicit object IntMonoid extends Monoid[Int] {
        def suma( x: Int, y: Int): Int = x + y
        def zero: Int = 0
      }

      // sumaLlista
      // el paràm. implícit selecciona instància segons el tipus del paràm. actual (a la crida)      
      // entre els ''implícit object''s visibles en l'àmbit.

      def sumaLlista[A](xs: List[A])(implicit m: Monoid[A]): A = 
         xs match {
              case Nil => m.zero
              case y :: ys => m.suma( y, sumaLlista( ys))
         }

      println( sumaLlista( List(1, 2, 3)))  // obté l'objecte implícit segons el tipus del paràmetre.
      println( sumaLlista( List(1, 2, 3)) (IntMonoid))   // explicita l'implícit quan n'hi ha diversos (Monoide per a la suma, per al producte, ...) 
      println( sumaLlista( List("a", "b", "c")))
    }
Restricció de context (Context bound) i obtenció d'instàncies amb implicitly[modifica | modifica el codi]

Introdueix el requeriment d'instanciació de classes de tipus (genèrics) en el paràmetre de tipus.

La restricció de context [A: Monoid] indica que cal que hi hagi una instància de Monoid per al tipus A, visible en l'àmbit d'ús.

Codificació alternativa de sumaLlista. La instància de Monoid s'obté amb implicitly.

      def sumaLlista[A: Monoid](xs: List[A]): A = {      

        val m = implicitly[Monoid[A]]     // obté una instància de Monoid per al tipus A 
        xs match {
             case Nil => m.zero
             case y :: ys => m.suma( y, sumaLlista( ys))
             }
        }
Restricció de context respecte a Paràmetre implicit - Ordered i Ordering[modifica | modifica el codi]

Ordered[A] i Ordering[A] ofereixen una funcionalitat similar

  • El tret Ordered[A] equival a la interfície "Ordenat" i implementa la ordenació bàsica del tipus i només se'n defineix una de sola. Es pot especificar com a requeriment de context
  • El tret Ordering[A] (Ordenació) equival a una classe de tipus instanciable en implicit objects per tipus, i en podem tenir diverses instàncies amb relacions d'ordre diferents (ascendent, descendent, basada en un component, ...). Per explicitar la que vulguem, s'ha de fer servir com a paràmetre implícit.
Injecció de mètodes[modifica | modifica el codi]

La conversió implícita d'un tipus a una classe o tret, té l'efecte d'augmentar els mètodes per al tipus que es converteix.[44]

object Test extends App {

  object Augmentador {

    class ToMyInt( s:String) {
      def toMyInt: Int = java.lang.Integer.parseInt(s)
    }

    implicit def string2ToMyInt(s: String) = new ToMyInt(s)
  }

  import Augmentador._  // incorpora l'implícit a l'àmbit

  println ("12".toMyInt) 
}
  • Augment ràpid (en escriure'l però més lent d'execució).[45]
object Test extends App {

  implicit def string2ToMyInt(s: String) = new {

    def toMyInt: Int = java.lang.Integer.parseInt(s)
  }

  println ("12".toMyInt) 
}

Convenció per indicar efectes col·laterals en els mètodes sense paràmetres[modifica | modifica el codi]

  • Per convenció, els mètodes sense paràmetres podran ometre els parèntesis, a la definició i en l'ús, per indicar que no inclouen efectes col·laterals.[46]
object Demo {
  val valor = ...
  def llegir() = ...   // efectes coŀlaterals, cal posar els parèntesis

  def obtenirValor = ... // no hi ha efectes coŀlaterals => no s'hi han de posar els parèntesis
}

Avaluació tardana (ang: lazy)[modifica | modifica el codi]

valors lazy val (retarda l'avaluació fins que fa falta en una expressió)
amb el qualificador lazy, l'expressió només s'avalua quan fa falta alguna variable del patró, i, a més a més, per l'ús de val memoritza el valor (aprofita el resultat en crides posteriors)
  lazy val patró = expressió
paràmetres per-nom (param: => tipus) (Call by name)
afegint => abans del tipus, aquests paràmetres s'avaluen substituint el nom del paràmetre en el codi per l'expressió del paràmetre actual.
object Main extends App {
  var cnt = 0
  def perNom( n: => Int) = {
          println( cnt); 
          println( n); // aquí se substitueix el nom del param. per l'expressió del param. actual
          println( n); // aquí també
      }
  perNom( { cnt += 1; cnt})
}

execució a l'intèrpret:

scala> Main
0
1
2
Vistes
Avaluació no-estricta de les col·leccions.[47]
Traversable.view
genera una "vista d'avaluació diferida" d'una col·lecció, resultant un tipus TraversableView. Les transformacions que s'hi apliquin (map, filter, etc.), en comptes de generar una nova estructura, quedaran pendents d'aplicació generant una nova vista, fins que s'hi apliqui el mètode force.[48]
Traversable.toIterator
permet recórrer una estructura una sola vegada (l'iterador muda de valor a cada 'next()'). Els elements s'obtenen per necessitat, evitant la generació d'estructures intermèdies en les transformacions successives de l'estil de map, filter, fold.[49]
Traversable.toStream
permet obtenir-ne els elements de manera tardana (per necessitat). Els streams es poden recórrer tants cops com calgui a partir d'una referència, per què hi ha memoïtzació (memorització en una taula dels resultats evitant el recàlcul).[50]

evitant la necessitat de desforestació[modifica | modifica el codi]

En l'aplicació funcional successiva, ex.: estructura.filtre( predicat).map(funcióDeTransformació).reducció( op_binària), les classes Iterator i Stream, en recuperar els elements de manera tardana, eviten la problemàtica de la desforestació (malversació d'espai per la creació d'estructures intermèdies temporals quan l'avaluació és estricta) i la necessitat d'escriure rutines multifunció filtreMapReducció(predicat, funcióDeTransformació, op_binària) d'un sol bucle per solucionar-ho.

Intervals numèrics[modifica | modifica el codi]

La classe Range permet definir iteradors (avaluació tardana) sobre seqüències numèriques de tipus Int.

La classe NumericRange[T] generalitza la classe Range a sencers de diferents precisions (Han d'implementar Integral[T] (anell unitari íntegre ordenat euclidià: ops. dels enters).

(0 to 2).toList         // interval tancat [0,2]:  List(0,1,2)
(0 until 2).toList      // interval obert per la dreta [0,2): List(0,1) 
(2 to 0 by -1).toList   // interval amb increment: List(2,1,0)

La API de Scala[modifica | modifica el codi]

Scala té una llarga biblioteca per facilitar la feina amb varietat de col·leccions immutables i mudables així com tractament de XML i altres que trobareu aquí.[51]

L'especificació oficial del llenguatge és aquí.[52]

Els elements predefinits són a l'object Predef[53]

Cercadors de l'API[modifica | modifica el codi]

La API de Scala www.scala-lang.org/api/current/ permet cercar per nom de paquet, o signatures d'àmbit global.

  • Hi ha un cercador d'índex de biblioteques a la mateixa web anomenat "Scaladex": index.scala-lang.org
  • La web Scalex.org permetia, fins fa poc, cercar els identificadors exportats per un paquet, de manera similar a Hayoo[54] o bé Hoogle[55] per al llenguatge Haskell. Tanmateix se'n pot descarregar el codi del Github.[56]

Biblioteques[modifica | modifica el codi]

Alguns paquets que abans estaven inclosos a la biblioteca bàsica, com ara scala.xml, ara s'han de referenciar separadament des del GitHub de Scala.

La llista de biblioteques de tercers és a Scala wiki - Tools and Libraries.

Scala Actors -- Concurrència amb el model d'Actors[modifica | modifica el codi]

Vegeu ref.[58]

STM (Transaccions de memòria per programari)[modifica | modifica el codi]

Vegeu ref.[59][60]

Col·leccions[modifica | modifica el codi]

Vegeu ref.[61]

Gràfic de les col·leccions immutables.

Gràfic de les col·leccions mudables.

paral·lelisme
Scala també disposa de col·leccions per al paraŀlelisme, per aprofitar la potència dels processadors multicor amb algorismes que utilitzen l'estratègia "dividir i vèncer".[62] El mètode par ofereix implementacions amb paraŀlelisme de la majoria de col·leccions que implementen el tret ParIterable[63] amb l'ajut d'un particionador (Splitter) que ofereix un iterador sobre les subcol·leccions disjuntes resultants de la partició.[64]
(col·lecció.par.map (_ * 2)).toList  // multiplica, paraŀlelitzant, els elements d'una col·lecció de numèrics [http://www.scala-lang.org/api/current/index.html#scala.math.Numeric]
// el factor 2 és convertit al tipus de l'element si existeix una conversió implícita
concurrència
Existeix una versió de diccionaris TrieMap (basats en arbres de prefix i taula de dispersió (ang:hash-array)) per a l'accés concurrent (simultani des de diversos fils d'execució) i lliure de baldes (ang:lock-free).[65]

operacions comunes dels mòduls estàtics (object) que acompanyen les col·leccions[modifica | modifica el codi]

Vegeu "Creating collections".[66]

// Generador de col·lecció buida
def empty[A]: Col·lecció[A]

// Generador partint d'elements oferts en una crida de nombre variable de paràmetres
def apply[A](xs: A*): Col·lecció[A]

// el nom apply es pot ometre, és el nom de funció per defecte
// Col·lecció( elem1, elem2, ...) equival a Col·lecció.apply( elem1, elem2, ...)

operacions comunes de les classes de les col·leccions[modifica | modifica el codi]

def isEmpty: Boolean
def nonEmpty: Boolean

// companion: 'Object' fàbrica d'instàncies de la col·lecció (generadors i altres mètodes estàtics)
def companion: GenericCompanion[Col·lecció]

def sameElements(that: GenIterable[A]): Boolean  // compara els elements amb la col·lecció 'that'

def iterator: Iterator[A]      // obté un iterador (mètodes: hasNext(), next()) sobre els elements

def mkString: String                 // mostra la col·lecció en una String
def mkString (sep: String): String    // ... intercalant separador

def toString(): String            // a String
def toArray: Array[A]             // a Array,  correspon als vectors del Java del tipus (A [])
def toBuffer[B >: A]: Buffer[B]   // a Buffer (magatzematge temporal incrementable)

def toSeq: Seq[A]                 // a seqüència, implementada amb List
def toSet[B >: A]: Set[B]         // a conjunt
def toList: List[A]               // a llista
def toIndexedSeq: IndexedSeq[A]   // a seqüència d'accés aleatori, implementada amb Vector
def toVector: scala.Vector[A]     // a vector, implementació per defecte de les IndexedSeq

def toMap[T, U]: collection.Map[T, U]   // a diccionari, si i només si els elements són parells (A ~ (T, U))

def toIterator: Iterator[A]       // obté un iterador (travessable un sol cop), equival al mètode 'iterator'
def toStream: Stream[A]           // obté un Stream (travessable més d'un cop)  
def toTraversable: collection.Traversable[A] // a Travessable (mètode forEach) // retorna la mateixa instància si la col·lecció ja ho és.

def to[Col[_]]: Col[A]            // a col·lecció explicitant el paràmetre de tipus
                                  
scala> (1 to 3).to[List]
res1: List[Int] = List(1, 2, 3)

scala> (1 to 3).to : List[Int]    // o bé mitjançant una restricció de tipus 
res2: List[Int] = List(1, 2, 3)
  • Seqüències[67] (entre d'altres):
// apply: obtenir element enèsim
// el nom apply es pot ometre, és el nom de funció per defecte:
// l'element de la col·lecció xs escrit xs(n) equival a xs.apply(n)

def apply( idx: Int): A               // obtenir l'element a l'índex 'idx'

def isDefinedAt( idx: Int): Boolean    // està definit per a l'índex 'idx' ?

def reverseIterator: Iterator[A]    // iterador en sentit invers
  • IndexedSeq: seqüències d'accés aleatori: ArrayBuffer, Vector, ...
  • LinearSeq: seqüències d'accés lineal (d'un amb un)
  • avaluació estricta: List, Queue
  • avaluació tardana: Iterator, Stream
  • avaluació diferida: TraversableView (difereix l'avaluació dels mètodes aplicats, fins que s'aplica .force o se'n demana la conversió a una col·lecció concreta)[68]
  • Altres signatures: Traversable, Iterable, Set, Map

Interfície TraversableOnce[modifica | modifica el codi]

Vegeu ref.[69]

  • Reducció
    // A és el tipus de l'element de la col·lecció

    def foldLeft[B](z: B)(op: (B, A)  B): B    // plegat per l'esquerra sobre valor inicial
    def foldRight[B](z: B)(op: (A, B)  B): B   // plegat per la dreta sobre valor inicial

    // [A1 >: A] vol dir que els paràmetres es tipifiquen per algun tipus A1 supertipus de A

    // si l'operació del plegat és associativa en el domini dels elements, 
    // tindrem el mateix resultat per l'esquerra com per la dreta, per tant no cal explicitar-ho.
    def fold[A1 >: A](z: A1)(op: (A1, A1)  A1): A1    // plegat endomòrfic sobre valor inicial amb op. associativa
    
    // (z /: xs) equival a (xs foldLeft z) operador associatiu per la dreta (xq. acaba en ':')
    def /:[B](z: B)(op: (B, A)  B): B   

    // (xs :\ z) equival a (xs foldRight z) op. associatiu per l'esquerra.
    def :\[B](z: B)(op: (A, B)  B): B   

    def reduceLeft[B >: A](op: (B, A)  B): B       // plegat per l'esquerra de la cua sobre el cap (coŀl. no buida); func. parcial !!        
    def reduceLeftOption[B >: A](op: (B, A)  B): Option[B]      // versió total (coŀl. buida => None)

    def reduce[A1 >: A](op: (A1, A1)  A1): A1    // plegat endomòrfic de la cua sobre el cap, amb op. associativa (coŀl. no buida); func. parcial !!
    def reduceOption[A1 >: A](op: (A1, A1)  A1): Option[A1]     // versió total (coŀl. buida => None)

    // plegats numèrics
    def sum[A: Numeric]: A
    def product[A: Numeric]: A

    // plegats; el tipus de l'elem ha d'ésser subtipus d'algun que implementi Ordering 
    //    quina instància d'ordenació (ord. ascendent, descendent, s/. l'ordre d'un component) es pot adjuntar com a param.
    def min[B >: A](implicit cmp: Ordering[B]): A
    def max[B >: A](implicit cmp: Ordering[B]): A

    // plegats via funció de projecció amb imatge ordenable quina instància d'ordenació es pot adjuntar com a param.
    def minBy[B](f: (A)  B)(implicit cmp: Ordering[B]): A
    def maxBy[B](f: (A)  B)(implicit cmp: Ordering[B]): A

Interfície Traversable[modifica | modifica el codi]

TraversableLike[70] caracteritzada per definir foreach i que conté la major part dels mètodes de Traversable, afegeix a TraversableOnce, a banda d'altres trets, els mètodes següents

  • Consulta
    // A és el tipus de l'element de la col·lecció de tipus Repr

    def isEmpty: Boolean             // és buida ?
    def nonEmpty: Boolean            // és no buida ?
    def size: Int                    // mida

    def hasDefiniteSize: Boolean     // és finita la col·lecció ? 
    def isTraversableAgain: Boolean  // és travessable altra vegada? (els iteradors només una) 

    // com a seqüència
    def head: A                      // cap, funció parcial!!! excepció NoSuchElementException si col. buida
    def headOption: Option[A]        // cap, versió total
    def tail: Repr                   // cua, funció parcial!!! excepció NoSuchElementException si col. buida

    // quantificadors amb predicat
    def exists (predicat: (A)  Boolean): Boolean  // algun element compleix el predicat?
    def forall (predicat: (A)  Boolean): Boolean  // el compleixen tots?
    def count (predicat: (A)  Boolean): Int       // compta els que compleixen

    // cerca per predicat
    def find (predicat: (A)  Boolean): Option[A]  // cerca per predicat 

    // altres
    def last: A    // darrer. funció parcial!!! excepció NoSuchElementException si col. buida
    def lastOption: Option[A]  // darrer, versió total
    def init: Repr // descarta el darrer. funció parcial!!! excepció UnsupportedOperationException si col. buida

    // tails: successió de subcoŀleccions escapçant pel davant
    def tails: Iterator[Repr]  // equival a ((0 to col.size).map (col.drop))

    // inits: successió de subcoŀleccions escapçant pel darrere
    def inits: Iterator[Repr] //  equival a ((col.size to 0 by -1).map (col.take))
  • Combina
    def ++ [B](that: GenTraversableOnce[B]): Traversable[B]  // (xs ++ that) concatena associatiu per l'esquerra

    // els operadors acabats en ':' (associatius per la dreta) en notació infix, són mètodes de l'operand de la dreta 

    def ++: [B](that: TraversableOnce[B]): Traversable[B]    // (that ++: xs) concatena associatiu per la dreta
  • Transforma
    def foreach [U](f: Elem => U): Unit   // iteració amb funció d'efectes laterals
    def map [B](f: (A)  B): Traversable[B]  // mapeja (obté la col·lecció de les imatges de l'aplicació f)

    // encadena amb funció de resultat múltiple (col·lecció) i concatena resultats 
    def flatMap [B](f: (A)  GenTraversableOnce[B]): Traversable[B]  // equival a l'encadenament monàdic del llenguatge Haskell (>>=)

    // mapeig amb funció definida parcialment (definides amb encaix de patrons, que pot ser incomplet)
    // collect: obté, per als elements vàlids en el subdomini de 'pf', la col·lecció de les imatges 
    def collect [B](pf: PartialFunction[A, B]): Traversable[B]   
    def collectFirst [B](pf: PartialFunction[A, B]): Option[B]   // obté, la primera de les imatges dels elements vàlids en el subdomini de 'pf'
  • Partició
    def slice (from: Int, until: Int): Repr  // subcoŀlecció corresp. a l'interval obert per la dreta [from .. until) (exclou límit superior)

    // a l'índex
    def take (n: Int): Repr  // prefix de n elements
    def drop (n: Int): Repr  // escapça el prefix de n elements
    def splitAt (n: Int): (Repr, Repr)  // partició a l'índex, com {col => (col.take(n), col.drop(n))}

    // segons predicat
    def filter (predicat: (A)  Boolean): Repr // subcoŀlecció dels que compleixen 
    def partition (predicat: (A)  Boolean): (Repr, Repr) // parteix com {col => (col.filter (predicat), col.filter (not predicat))}

    def takeWhile (predicat: (A)  Boolean): Repr // prefix mentre elements compleixen
    def dropWhile (predicat: (A)  Boolean): Repr // sufix de takeWhile( predicat)

    def span (predicat: (A)  Boolean): (Repr, Repr)   // parteix com {col => (col.takeWhile(predicat), col.dropWhile(predicat))}

    // agrupa els elements en funció de les imatges de 'f', en un diccionari de subcoŀleccions
    def groupBy [K](f: (A)  K): immutable.Map[K, Repr]

    // monàdic, per a ser emprat a les clàusules 'for' i llistes per comprensió (encadenament monàdic)
    def withFilter (predicat: (A)  Boolean): FilterMonadic[A, Repr]   // filtre monàdic

    // TraversableView difereix les transformacions posteriors fins que s'aplica '.force' o es converteix a col·lecció concreta.
    def view: TraversableView[A, Repr] // vista d'avaluació diferida
    def view (from: Int, until: Int): TraversableView[A, Repr]  // llesca d'avaluació diferida
  • Reducció
    // successió de plegats parcials per l'esquerra
    def scanLeft [B, That](z: B)(op: (B, A)  B)(implicit bf: CanBuildFrom[Repr, B, That]): That 

    // successió de plegats parcials per la dreta
    def scanRight [B, That](z: B)(op: (A, B)  B)(implicit bf: CanBuildFrom[Repr, B, That]): That  

    // scan: successió de plegats parcials amb op. associativa (tant és si el plegat és per la dreta o per l'esquerra)
    def scan [B >: A, That](z: B)(op: (B, B)  B)(implicit cbf: CanBuildFrom[Repr, B, That]): That 

    // concatena elements de col·lecció de col·leccions travessables
    def flatten [B](implicit asTraversable: (A)  GenTraversableOnce[B]): Traversable[B]

El tret CanBuildFrom[modifica | modifica el codi]

El tret CanBuildFrom[-ColOrigen, -Elem, +ColDestí] descriu una interfície per optimitzar la generació de col·leccions des d'altres col·leccions amb implementacions existents per a tipus específics mitjançant objectes implícits.[71]

És d'interés quan com a sortida d'un mètode es vol crear una col·lecció diferent, determinada pel tipus del resultat.

Interficie Iterable[modifica | modifica el codi]

IterableLike, caracteritzada per definir un iterador que faciliti visitar els elements, i que conté la major part dels mètodes d'Iterable[72] afegeix a TraversableLike entre d'altres els següents:

  • partició:
def takeRight(n: Int): Repr              // obtenir sufix de llargada n
def dropRight(n: Int): Repr              // escapçar el sufix

def grouped(n: Int): Iterator[Repr]   // retorna iterador de trams de llargada fixa n
  • combinació:
def zip[B](that: GenIterable[B]): GenIterable[(A, B)]   // aparellament per posició amb altra col·lecció

// zipAll: zip igualant llargades amb farciment
def zipAll[B](that: collection.Iterable[B], thisElem: A, thatElem: B): GenIterable[(A, B)] 

def zipWithIndex: GenIterable[(A, Int)]    // aparella amb l'índex (partint de zero)

mètodes de consulta específics[modifica | modifica el codi]

// a les seqüències
def apply (n: Int): A  // obtenir elem. enèsim partint de zero: xs(3). funció parcial!!! amb IndexOutOfBoundsException

// comprova relació dels elements amb el corresponent posicional d'una segona seqüència
def corresponds [B](that: GenSeq[B])(predicat: (A, B)  Boolean): Boolean

// als conjunts
def contains (key: A): Boolean
def subsetOf (that: GenSet[A]): Boolean  // és subcjt de ?

def subsets (len: Int): Iterator[Set[A]]  // subcjts de llarg. específica
def subsets: Iterator[Set[A]]             // tots els subconjunts

// als diccionaris
def get (key: A): Option[B]

// als opcionals ''Option[+A]''
def getOrElse[B >: A](default:  B): B      // cas de la variant (Some x) n'extreu el valor, altrament avalua el param.

def toRight[T](left:  T):Either[T, A]      // cas de la variant (Some x) torna Right( x), altrament Left amb el param.
def toLeft[T](right:  T):Either[A, T]      // cas de la variant (Some x) torna Left( x), altrament Right amb el param.

// al dual ''Either[+A, +B]( Left(l:A) | Right(r:B) )''
left.getOrElse [AA >: A](or:  AA): AA      // cas de la variant (Left a) n'extreu el valor, altrament avalua param.
right.getOrElse [BB >: B](or:  BB): BB     // cas de la variant (Right b) n'extreu el valor, altrament avalua param.

left.toOption: Option[A]    // cas de Left( a) torna Some( a), altrament None
right.toOption: Option[B]   // cas de Right( b) torna Some( b), altrament None

// al dual ''Try[+T] d'avaluació d'expressions que poden llançar excepcions
//         avaluant a  Success(resultat:T) | Failure(excepció: Throwable)'' 
def get: T     // cas de la variant (Success resultat) retorna resultat, cas de (Failure excep) llança l'excepció
def getOrElse [U >: T](default:  U): U  // cas de la variant (Success r) n'extreu el valor, altrament avalua el param.

def toOption: Option[T]   // cas de Success( resultat) retorna Some( resultat) altrament None
def contains(elem: Any): Boolean  // està contingut?

def distinct: Seq[A]              // diferents (elimina elements repetits)

def diff (that: Seq[A]): Seq[A]  // multi-diferència, per cada elem. del de la dreta, elimina una ocurrència.
def union (that: Seq[A]): Seq[A]      // multi-unió (concatena)
def intersect (that: Seq[A]): Seq[A]  // multi-intersecció

def combinations (n: Int): Iterator[Seq[A]]
def permutations: Iterator[Seq[A]]
  • seqüències, accés indexat [0..
def indices: immutable.Range // retorna rang d'índexs

// obtenir element per posició (basada en 0), ''apply'' és el nom de funció per defecte i es pot ometre: xs(n)
def apply (n: Int): A   // Funció parcial!!! IndexOutOfBoundsException

// cerca posició per valor
def indexOf (elem: A [, from: Int]): Int    // retorna índex o bé (-1)
def lastIndexOf (elem: A [, end: Int]): Int

// cerca posició per predicat
def indexWhere (predicat: (A)  Boolean [, from: Int]): Int
def lastIndexWhere (predicat: (A)  Boolean [, end: Int]): Int

// actualització funcional  amb associació (índex, elem)
def updated [A1 >: A](index: Int, elem: A1): Repr   // retorna nova col·lecció actualitzada
  • seqüències, consulta de subseqüències
def slice (from: Int, until: Int):Seq[A]  // subseq. a l'interval obert per la dreta

def containsSlice [B](that: GenSeq[B]): Boolean  // conté subseq. ?

def startsWith [B](that: GenSeq[B], offset: Int): Boolean  // comprova prefix
def endsWith [B](that: GenSeq[B]): Boolean                 // comprova sufix

// cerca posició de subseq.
def indexOfSlice [B >: A](that: GenSeq[B] [, from: Int]): Int     // retorna pos. o bé (-1)
def lastIndexOfSlice [B >: A](that: GenSeq[B] [, end: Int]): Int  // retorna pos. o bé (-1)

// llarg. de subseq. corresp. al predicat
def prefixLength (predicat: (A)  Boolean): Int
def segmentLength (predicat: (A)  Boolean, from: Int): Int

// obtenció de subseqüències desplaçant una finestra de la mida esmentada
def sliding (size: Int): Iterator[Seq[A]] 
def sliding (size: Int, step: Int): Iterator[Seq[A]]   // step: especifica la distància entre inicis
  • seqüències, ordenació
def sortWith (lt: (A, A)  Boolean): Seq[A]   // especificant la relació d'ordre

// sorted: si l'element és subtipus d'un ordenable, quina instància d'ordenació podem especificar
def sorted [B >: A, B](implicit cmp: Ordering[B]): Seq[A]  

// ordena segons la imatge ordenable d'una projecció quina instància d'ordenació podem especificar
def sortBy [B](f: (A)  B)(implicit cmp: Ordering[B]): Seq[A]

operadors d'actualització[modifica | modifica el codi]

Els operadors binaris en notació infix i associatius per la dreta (aquells quin símbol acaba en ':') s'avaluen sintàcticament com a mètodes de l'operand de la dreta. (exemple: x1 +: (x2 +: xs); la col·lecció va a la dreta quan l'operador acaba en ':')

a les seqüències[modifica | modifica el codi]

per a les seqüències dels tipus immutable.Seq[+A] i mutable.Seq[A].

funcionals purs a les seqüències[modifica | modifica el codi]
  • Operadors i mètodes funcionals purs (sense efectes col·laterals) de les seqüències (immutables o mudables)
retornen nova seqüència
x +: xs  // nova seq. afegint al capdavant (associatiu per la dreta xq. acaba en ':')
xs :+ x  // nova seq. afegint pel final

xs ++ (ys: GenTraversableOnce[A]) // nova seq. concatenant amb elements de col·lecció
(ys: TraversableOnce[A]) ++: xs  // nova seq. prefixant elems. de col·lecció (op. associatiu per la dreta)

// updated: actualització funcional que retorna nova seq. actualitzada
// cas de col·lecció covariant en el tipus de l'elem Col[+A], l'argument (posició contravariant) 
// ha d'admetre tipus més generals: A1 tals que (A1 >: A).
// altrament A1 = A
def updated [A1 >: A](index: Int, elem: A1): Repr   // actualitza amb elem. a l'índex

// patch (apedaçar): retorna còpia inserint seqüència ''that'' a l'índex ''from'', substituint ''replaced'' elements
def patch (from: Int, that: GenSeq[A], replaced: Int): Repr 

// combina com a multi-conjunt
xs intersect ys // multi-intersecció
xs diff ys      // multi-diferència
xs union ys     // multi-unió (concatenació)

def distinct: Seq[A]              // diferents (nova seq. eliminant repetits)

// transposició d'una seqüència de seqüències
def transpose [B](implicit asTraversable: (A)  GenTraversableOnce[B]): Seq[Seq[B]]
mutadors a les seqüències[modifica | modifica el codi]
  • Operadors mutadors de les seqüències mudables (ListBuffer[A] i MutableList[A])
x +=: xs // afegir pel davant (associatiu per la dreta)

xs += x  // afegir al final
xs -= x  // eliminar

xs.+= (x, y, ...)  // afegir diversos (llista d'arguments variable)
xs.-= (x, y, ...)  // eliminar diversos

xs ++= (ys: TraversableOnce[A])  // afegir elems. de col·lecció travessable
xs --= (ys: TraversableOnce[A])  // eliminar elems de col·lecció travessable

def transform(f: (A)  A): Seq.this.type // aplica una funció als elements de la seqüència

// només a ListBuffer
(ys: TraversableOnce[A]) ++=: xs // afegeix al capdavant elements de col·lecció
als Buffer[modifica | modifica el codi]

Buffer: estructura mudable de magatzematge temporal incrementable que permet eliminacions.

Per al tipus mutable.Buffer[A].[73]

  • Mètodes mutadors als Buffers, addicionals als esmentats prèviament per a les seqüències
def append (elems: A*): Unit                         // afegeix diversos (llista d'arguments variable)
def appendAll (ys: TraversableOnce[A]): Unit         // afegeix de col·lecció 

def prepend (elems: A*): Unit                        // prefixa diversos
def prependAll (ys: TraversableOnce[A]): Unit        // prefixa de col·lecció

def insert (n: Int, elems: A*): Unit                // insereix diversos a la posició n
def insertAll (n: Int, ys: Traversable[A]): Unit   // insereix de col·lecció a la pos. n

def update (n: Int, newelem: A): Unit                // actualitza l'enèsim
def remove (n: Int): A                               // elimina l'enèsim

def trimStart (n: Int): Unit                         // retalla pel davant n elems.
def trimEnd (n: Int): Unit                           // retalla pel darrere n elems.

def clear(): Unit                                   // buida
a les seqüències d'accés aleatori, anomenades indexades[modifica | modifica el codi]

per a seqüències d'accés aleatori dels tipus immutable.IndexedSeq[+A] i mutable.IndexedSeq[A]

  • Mètodes funcionals a les seq. indexades IndexedSeq (mudables o immutables)

updated (mode verbal participi) és la versió funcional del mutador update (verb en mode imperatiu), descrites tot seguit:

// updated: actualització funcional que retorna una nova seq. indexada amb l'actualització
// cas de col·lecció covariant a l'elem. (+A), requereix que el tipus de l'elem. a l'argument (posició contravariant) 
// ha d'admetre tipus més generals: A1 tals que (A1 >: A).
// altrament A1 = A
def updated[A1 >: A](índex: Int, elem: A1): IndexedSeq[A]  // retorna nova seq. d'accés aleatori
  • Mètodes mutadors a les seq. indexades mudables mutable.IndexedSeq
def update(índex: Int, elem: A): Unit             // actualitza la posició indexada
a les cues[modifica | modifica el codi]

per a les cues dels tipus immutable.Queue[+A] i mutable.Queue[A].

funcionals purs a les cues[modifica | modifica el codi]
  • Operadors i mètodes funcionals purs (sense efectes col·laterals) de les cues (immutables o mudables)
retornen nova cua.
xs :+ x  // nova cua, encuant al final
x +: xs  // nova cua, encuant al capdavant (associatiu per la dreta xq. acaba en ':')

xs ++ (ys: GenTraversableOnce[A]) // nova cua encuant elements de col·lecció
(ys: TraversableOnce[A]) ++: xs  // nova cua prefixant elems. de col·lecció (op. associatiu per la dreta)
  • Operadors i mètodes funcionals purs (sense efectes col·laterals) només de les cues immutables.
def enqueue[B >: A](elem: B): Queue[B]             // encua un element retornant nova cua
def enqueue[B >: A](iter: Iterable[B]): Queue[B]   // encua els elements d'una col·lecció iterable.

def dequeue: (A, Queue[A])                         // descompon la cua NO BUIDA en parell (cap, resta) // funció parcial !!
def dequeueOption: Option[ (A, Queue[A]) ]             // versió total
mutadors a les cues[modifica | modifica el codi]
  • Operadors mutadors de les cues mudables (mutable.Queue[A])
xs += x  // encua al final
x +=: xs  // encua al capdavant (associatiu per la dreta)

xs.+= (x, y, ...)  // encua diversos (llista d'arguments variable)

xs ++= (ys: TraversableOnce[A])  // encua elems. de col·lecció travessable

def enqueue(elems: A*): Unit              // encua diversos (llista d'arguments variable)
def dequeue: A                            // desencua el cap i el retorna.

// segons predicat.
def dequeueFirst(predicat: (A)  Boolean): Option[A]     // desencua el primer que compleix el predicat
cues amb prioritat[modifica | modifica el codi]

Cues amb prioritat definida per un ordre implícit o bé explícit. mutable.PriorityQueue[A]

Els elements s'encuen davant d'aquells que són menors en l'ordre.

scala> import scala.collection.mutable.{PriorityQueue => PQ}
import scala.collection.mutable.{PriorityQueue=>PQ}

// amb ordre implícit, prioritat al més gran
scala> PQ(3,5,2).head
res2: Int = 5

scala> val intOps = implicitly[Integral[Int]]   -- Integral[T]: ops. dels enters (anell euclidià)
intOps: Integral[Int] = scala.math.Numeric$IntIsIntegral$@3a20f22d

// ordenació explícita per establir prioritat al més petit
scala> PQ(3,5,2)(intOps.reverse).head
res8: Int = 2
  • Operadors mutadors de les mudables "cues amb prioritat" (mutable.PriorityQueue[A])
xs += x  // encua un elem.

xs.+= (x, y, ...)  // encua diversos (llista d'arguments variable)

xs ++= (ys: TraversableOnce[A])  // encua elems. de col·lecció travessable

def enqueue(elems: A*): Unit              // encua diversos (llista d'arguments variable)
def dequeue: A                            // desencua el cap i el retorna.

També es pot definir l'element com a parell (prioritat, valor) definint l'ordre per comparació del primer de la tupla. Vegeu exemple.

als conjunts[modifica | modifica el codi]

per a conjunts del tipus immutable.Set[A] i mutable.Set[A].[74]

funcionals purs als conjunts[modifica | modifica el codi]
  • Operadors funcionals purs (sense efectes col·laterals) dels conjunts (Set) (immutables o mudables)
retornen nou conjunt
xs + x  // actualitza (unió) amb {x}
xs - x  // diferència amb {x}

xs.+ (x, y, ...)  // actualitza amb diversos elements (llista d'arguments variable)
xs.- (x, y, ...)  // diferència amb diversos

xs ++ (ys: GenTraversableOnce[A]) // actualitza amb elements de col·lecció
xs -- (ys: GenTraversableOnce[A]) // diferència conjunt col·lecció

(ys: TraversableOnce[A]) ++: xs  // actualització, associatiu per la dreta

xs & ys; xs intersect ys // intersecció de conjunts
xs &~ ys; xs diff ys     // diferència de conjunts
xs | ys; xs union ys     // unió de conjunts
mutadors als conjunts[modifica | modifica el codi]
  • Operadors mutadors dels conjunts mudables
xs += x  // inserir
xs -= x  // eliminar

xs.+= (x, y, ...)  // inserir diversos (llista d'arguments variable)
xs.-= (x, y, ...)  // eliminar diversos

xs ++= (ys: Traversable[A])  // inserir elems de col·lecció
xs --= (ys: Traversable[A])  // elimina elems de col·lecció
als diccionaris[modifica | modifica el codi]

per a diccionaris (taules associatives) amb tipus immutable.Map[A, +B] i mutable.Map[A, B].[75]

associació és una denominació de la correspondència clau valor
funcionals purs als diccionaris[modifica | modifica el codi]
  • Operadors funcionals purs (sense efectes col·laterals) dels diccionaris (Map) (immutables o mudables)
retornen nou diccionari
dicc + ((k, v))  // actualitza dicc (unió) amb diccionari singular de parell (clau, valor)
dicc - k  // elimina (per diferència) el parell de la clau k

dicc.+ ((k1, v1), (k2, v2), ...)  // actualitza amb diversos parells (llista d'arguments variable)
dicc.- (k1, k2, ...)  // elimina els parells quines claus s'esmenten

dicc ++ (ys: GenTraversableOnce[(A, B)]) // actualitza amb col·lecció d'associacions
dicc -- (ys: GenTraversableOnce[A]) // elimina claus de col·lecció

(ys: TraversableOnce[(A, B)]) ++: dicc  // actualitza, associatiu per la dreta

// updated: actualització funcional que retorna nou diccionari actualitzat
// cas de col·lecció covariant a l'elem.(+B), requereix que el tipus de l'elem. a l'argument (posició contravariant) 
// ha d'admetre tipus més generals: B1 tals que (B1 >: B).
// altrament B1 = B
dicc.updated[B1 >: B](key: A, value: B1): Map[A, B1]  // actualitza amb associació (k, v)
mutadors als diccionaris[modifica | modifica el codi]
  • Operadors mutadors dels diccionaris mudables
dicc += ((k, v))  // afegeix associació de parell (clau, valor)
dicc -= k  // elimina associació de clau k

dicc.+= ((k1, v1), (k2, v2), ...)  // incorpora diversos parells (llista d'arguments variable)
dicc.-= (k1, k2, ...)  // elimina diverses claus

dicc ++= (ys: GenTraversableOnce[(A, B)]) // incorpora associacions de col·lecció de parells
dicc --= (ys: GenTraversableOnce[A]) // elimina claus de la col·lecció

dicc.update(key: A, value: B): Unit

Strings, Plantilles i Expressions regulars[modifica | modifica el codi]

String com a seqüència[modifica | modifica el codi]

El tipus String és augmentat implícitament a StringOps[76] permetent aplicar-hi la interfície IndexedSeqLike[77] (tractant String com una seq. d'accés aleatori). A més a més, incorpora els mètodes següents:

def lines: Iterator[String]    // parteix en línies
def linesWithSeparators: Iterator[String]   // parteix ... incorporant separadors de línia, siguin {LF, CRLF, FF}

def r: Regex    // interpreta la cadena com a patró d'expressió regular, obtenint-ne un valor Regex
def r (groupNames: String*): Regex  // ... donant noms als grups 

scala> """(\d\d)-(\d\d)-(\d\d\d\d)""".r("mes", "dia", "any").findFirstMatchIn("12-31-1999").map( _.group( "dia"))
res0: Option[String] = Some(31) 

def capitalize: String   // retorna String amb majúscula al primer caràcter

// to{tipus}: interpreta contingut com a literal del tipus, llançant excepció si no concorda en tota l'extensió.
def {toBoolean, toByte, toShort, toInt, toLong, toFloat, toDouble} 

// format: utilitza el contingut de la cadena com a format per mostrar args, igual que 'sprintf' de C
def format(args: Any*): String 

def padTo(len: Int, elem: A): String[A]   // farceix al final fins a llargada 'len' amb 'elem' 

def stripPrefix (prefix: String): String   // escapça el prefix
def stripSuffix (suffix: String): String   // escapça el sufix

// 'stripMargin' elimina marge esquerre (blancs i tabuladors) fins a un delimitador de marge,
// d'aquelles línies de la cadena que continguin el delimitador!!

def stripMargin: String      // elimina marge esquerre delimitat per '|', incloent el delimitador
def stripMargin (marginChar: Char): String   // elimina marge esq. ... utilitzant el delimitador especificat en comptes de '|'

def stripLineEnd: String   // elimina {LF, CRLF, FF} del final

// diversos mètodes de java.lang.CharSequence
def charAt(arg0: Int): Char 
def length(): Int
def subSequence(arg0: Int, arg1: Int): CharSequence 

// diversos mètodes de java.lang.String
def matches(arg0: String): Boolean 
def regionMatches(arg0: Boolean, arg1: Int, arg2: String, arg3: Int, arg4: Int): Boolean  
def replaceFirst(arg0: String, arg1: String): String 
def replace(arg0: Char, arg1: Char): String 
def replace(arg0: CharSequence, arg1: CharSequence): String 
def replaceAll(arg0: String, arg1: String): String 
def substring (arg0: Int): String 
def substring (arg0: Int, arg1: Int): String 
def trim(): String 
def toCharArray(): Array[Char] 
def to{Lower|Upper}Case (): String             // Minúscules/Maj.           
def to{Lower|Upper}Case (arg: java.util.Locale): String   // Min./Maj. segons 'Locale'
...

Strings especials[modifica | modifica el codi]

String multilínia, anomenat "document tot seguit" (ang:heredoc), amb cometes triples. La barra '\' invertida no s'hi interpreta com a caràcter d'escapament, permetent escriure-hi expressions regulars.

""" Primera línia
continuació"""

""" Primera línia, "entrecomillat"
    |continuació amb marge""".stripMargin       // stripMargin elimina el marge esquerre fins al delimitador '|'

""" Si no volem el delimitador de marge '|'
    #continuació amb marge i delimitador especial""".stripMargin('#')   // ... per al delimitador de marge especificat

// per al cas d'expressions regulars
"""(\d\d\d\d)-(\d\d)-(\d\d)"""  // Aquí la barra invertida NO s'interpreta com a caràcter d'escapament

"tira normal\n" // Aquí la barra invertida sí que s'interpreta com a caràcter d'escapament

Plantilles[modifica | modifica el codi]

Plantilles amb interpolació, identificades per un nom de plantilla.[78]

// La plantilla "s" com a: s"Hola, $nom !!"
val nom = "Jaumet"
println(s"Hola, $nom !!")  // Hola Jaumet !!

// si l'expressió a interpolar és més complexa, cal delimitar-la entre claus
scala> val benvinguda = "benvingut !!"
scala> s"Hola $nom, ${benvinguda.capitalize}"
res1: String = Hola Jaumet, Benvingut !!

// La plantilla "f" permet afegir-hi especificadors de format com els del "printf" del llenguatge C
scala> val edat = 10
scala> f"En $nom%15s té $edat%3d anys !!"
res2: String = En          Jaumet   10 anys !!

Expressions regulars[modifica | modifica el codi]

  • El tipus Regex.[79]

A l'avaluador d'expressions:

scala> val regexDataISO = """(\d\d\d\d)-(\d\d)-(\d\d)""".r     // expr. de tipus Regex
regexData: scala.util.matching.Regex = (\d\d\d\d)-(\d\d)-(\d\d)

scala> "2004-01-20" match {case regexDataISO (any, mes, dia) => s"allò va passar el $dia/$mes/$any."; case _ => "no s'ha trobat cap data!!"}
res4: String = allò va passar el 20/01/2004.

// per al cas de voler caçar múltiples ocurrències

scala> (for ( m <- regexDataISO findAllMatchIn "2004-03-01, 2005-12-02") yield { m.subgroups match {case List(any, mes, dia) => s"$dia/$mes/$any"}}).toList
res5: List[String] = List(01/03/2004, 02/12/2005)

Biblioteques rellevants[modifica | modifica el codi]

scala-arm
[38] control d'excepcions en la gestió de recursos amb alliberament automàtic en acabar. Evita, en cas d'adquisició de múltiples recursos, la implantació de construccions try-finally una dins l'altra.
import resource._

// dos recursos, còpia de input a output, amb alliberament automàtic

for( input <- managed(new java.io.FileInputStream("entrada.txt"); 
     output <- managed(new java.io.FileOutputStream("sortida.txt")) {

  val buffer = new Array[Byte](512)
  def read(): Unit = input.read(buffer) match {
    case -1 => ()
    case  n => output.write(buffer,0,n); read()
  }
  read()
}
scalaz
[80][81] biblioteca de programació segons teoria de categories, com al llenguatge Haskell (Monoides, Functors, Functors aplicatius, Mònades, Fletxes, ...)
slick
[82] aporta col·leccions específiques per a l'accés a BDD relacionals generant SQL a partir de mètodes funcionals.
val explicitInnerJoin = for {
  (c, s) <- coffees join suppliers on (_.supID === _.id)    // FROM coffees AS c INNER JOIN suppliers AS s ON c.supid = s.id
  if c.name like "%expresso%"  // WHERE c.name LIKE "%expresso%"
  sortBy (s.price.desc)     // ORDER BY s.price DESC
  drop (20)                    // OFFSET n
  take (10)                    // LIMIT m
} yield (c.name, s.name)       // SELECT c.name, s.name

Entorns de desenvolupament[modifica | modifica el codi]

  • La manera més vistosa és descarregar l'entorn web de desenvolupament Activator de lightbend.com (abans TypeSafe.com).[83]
  • Per treballar a consola de comandes, cal descarregar el gestor de projectes SBT que per cada projecte descarregarà si no la té, la versió de Scala especificada pel fitxer de projecte (build.sbt). És convenient seguir l'estructura de directoris especificada per l'SBT.
  • Una altra manera de treballar amb Scala és descarregant d'aquí[2] l'endollable per a l'Eclipse (o altres entorns de desenvolupament Java) que ja incorpora el compilador i les biblioteques de les quals, clicant, se'n pot examinar el codi font.

Scala a l'Eclipse[modifica | modifica el codi]

A l'Eclipse,[84] un cop incorporat l'endollable al menú Help->Install new software podrem crear un "Projecte Scala" que incorporarà automàticament les biblioteques de Scala al projecte si ho fem amb el menú de context de la barra lateral esquerre a l'opció "New -> Project -> Scala Wizards -> Scala Project" del menú de context de la barra lateral de projectes.

Scala segueix la mateixa estructura de mòduls (package) que el Java. Tanmateix podem posar diversos objects, classes i traits al mateix fitxer de codi font.

És habitual crear primer sobre el directori src, abreviació de source (codi font), un Package amb nom on crear els fonts, si volem facilitar l'estructuració modular o la inter-operabilitat amb paquets d'altres projectes.

Després podrem triar, al menú de context del package, els assistents (ang:wizards) de generació d'esquelets Scala Application, Scala Object, Scala Class o Scala Trait

Execució: per cada projecte cal crear una configuració d'engegada que podrem desar, especificant projecte, nom de l'objecte que conté el mètode main i possibles paràmetres de l'aplicació. Clicant el menú de context del projecte, opcions "RunAs -> Run configurations -> especificar la configuració o seleccionar-la -> botó Run"

Exemples[modifica | modifica el codi]

Lector amb analitzador gramatical[modifica | modifica el codi]

Basat en la classe JavaTokenParsers[85] per aprofitar la decodificació de literals String.

La classe interna Parser[86] adequa els operadors ((~>) seqüència, (|) alternativa, postfix (*) zero o més repeticions, postfix (+) una o més, postfix (?) zero o una) als combinadors de gramàtica regular específics de la classe, i també els que permeten l'extracció (^^) o bé l'especificació de resultat (^^^).

package test              // fitxer: test/LaMevaApp.scala

/* Contingut del fitxer de dades amb el llenguatge de simulació de teclat que pretenem processar
Str "abcd"
Enter
Str "linia2"
Move Up
Move Dn
Move Left
Back
Move Right
Del
*/

object LaMevaApp extends App {

import scala.util.parsing.combinator.JavaTokenParsers

object MoveDirEnum extends Enumeration {
  type TMoveDir = Value
  val MoveDir_Up, MoveDir_Dn, MoveDir_Left, MoveDir_Right = Value
}

import MoveDirEnum._

// Simulació de pulsacions de teclat
// "sealed" permet comprovar exhaustivitat en l'encaix de patrons

abstract sealed class Cmd(); 
case class CmdStr( val str: String) extends Cmd() {
  override def toString = "CmdStr " ++ str
}
case class CmdEnter() extends Cmd() {
  override def toString = "CmdEnter"
}
case class CmdBack() extends Cmd() {
  override def toString = "CmdBack"
}
case class CmdDel() extends Cmd() {
  override def toString = "CmdDel"
}
case class CmdMove(val dir:TMoveDir) extends Cmd() {
  override def toString = "CmdMove " ++ dir.toString
}

def escapçaDelimitadors( s: String): String = {
     assume (s.length >= 2, "cas no previst"); // assumpció 
     s.slice( 1, s.length -1) // substring del segon al penúltim
     }

import scala.language.postfixOps

class MyParser extends JavaTokenParsers {
   
  // produccions
  def unOMesEspais = accept( ' ')+
  def zeroOMesEspais = accept( ' ')*
  def nl = zeroOMesEspais ~> accept( '\n')
  def strParam = stringLiteral ^^ { s => CmdStr( escapçaDelimitadors( s)) } // : Parser[Cmd]
  def str = "Str" ~> unOMesEspais ~> (strParam | err("Str: manca param"))
  def up = "Up" ^^^ MoveDir_Up  // : Parser[TMoveDir]
  def dn = "Dn" ^^^ MoveDir_Dn
  def right = "Right" ^^^ MoveDir_Right
  def left = "Left" ^^^ MoveDir_Left
  def moveDir = (up | dn | right | left) ^^ { dir => CmdMove( dir) }
  def move = "Move" ~> unOMesEspais ~> (moveDir | err( "Move: manca param"))
  def enter = "Enter" ^^^ CmdEnter()
  def back = "Back" ^^^ CmdBack()
  def del = "Del" ^^^ CmdDel()
  def statement : Parser[Cmd] = str | move | enter | back | del
  def statements : Parser[List[Cmd]] = rep1sep( statement, nl)
  }

object MyParser {

  import java.io.File
  import scala.io.Source

  def processaFitxer(parser:MyParser, nomFitxer: String): List[Cmd] =  {
        val file = new File( nomFitxer)
        if (file.exists()) {
                val contingut = Source.fromFile( file, "UTF-8").getLines().flatMap( _ ++: "\n").mkString
                parser.parseAll(parser.statements, contingut ) match {
                        case parser.Success(resultat,_) => resultat
                        case x => { println(x); Nil }
                }
        }
        else { printf( "el fitxer %s no existeix\n", nomFitxer); Nil
             }
        }
  }

val parser = new MyParser()

args.toList match {
     case nomFitxer :: Nil => // List( nomFitxer) també detecta llistes d'un sol element
                            { val cmds = MyParser.processaFitxer(parser, nomFitxer)
                              cmds match {
                                case Nil => {println( "no s'han trobat comandes"); sys.exit(1)}
                                case _ => { println("les comandes són: ")
                                            cmds.foreach( cmd => println( cmd))
                                          }
                                }
                            }

     case _ => println("ús: nomprog nomFitxer")
     }

}
# compilació a l'Eclipse: clicar al menú de context RunAs -> Run configurations 
#  -> doble-clic a Scala application -> New Configuration 
#  -> Ajustar el nom de l'objecte d'engegada (el que deriva de App o conté el mètode main) 
#  -> prémer botó Run
  
# compilació en consola de comandes
mkdir classes
scalac -d classes src/test/LaMevaApp.scala
# execució
scala -cp classes test.LaMevaApp

Cues amb prioritat sobre parells (prioritat, valor)[modifica | modifica el codi]

Cal especificar explícitament l'ordenació sobre el primer del parell.

import scala.math.Numeric._
import scala.collection.mutable.{PriorityQueue => PQ}

object Main extends App {
  
  val intOps = implicitly[ Integral[Int]]

  val cuaDeParellsAmbPrioritatAlPrimerDelParell_DeMajorAMenor = 
                    PQ((3,"b"),(1,"c"),(5,"a")) (intOps.on( _._1))  // Ordering explícit sobre el primer del parell

  val cuaDeParellsAmbPrioritatAlPrimerDelParell_DeMenorAMajor = 
                    PQ((3,"b"),(1,"c"),(5,"a")) (intOps.on( { case (k, v) => -k }))

  for (pq <- List( cuaDeParellsAmbPrioritatAlPrimerDelParell_DeMajorAMenor,
                   cuaDeParellsAmbPrioritatAlPrimerDelParell_DeMenorAMajor)) {println( "El cap és: " + pq.head)}
}

Referències[modifica | modifica el codi]

  1. Scala - descàrregues(anglès)
  2. 2,0 2,1 Endollables Scala a diversos entorns de desenv. (anglès)
  3. 3,0 3,1 trait Application obsolet a 2.9, ang:deprecated, eliminat a 2.11
  4. trait App
  5. 5,0 5,1 Scala - Opcions d'execució(anglès)
  6. 6,0 6,1 scala-sbt.org(anglès)
  7. SBT - .sbt build definition(anglès)
  8. SBT - Esquema de directoris(anglès)
  9. Scala Language Specification(anglès)
  10. Tipus unificats(anglès)
  11. ScalaObject ha quedat obsolet (ang:deprecated)(anglès)
  12. 12,0 12,1 scala.Nothing(anglès)
  13. API - Symbol(anglès)
  14. 14,0 14,1 Equality of objects
  15. scala.AnyRef(anglès)
  16. scala.Any(anglès)
  17. scala.collection.GenIterable(anglès)
  18. AnyRef.==(anglès)
  19. 19,0 19,1 Scala docum. - Variances(anglès)
  20. 20,0 20,1 20,2 Scala tour - Lower type bounds(anglès)
  21. Subtyping - Function types(anglès)
  22. 22,0 22,1 Liskov substitution principle(anglès)
  23. Cota superior en els tipus(anglès)
  24. Cota inferior en els tipus(anglès)
  25. Objectes extractors(anglès)
  26. Introduction to Monads in Scala(anglès)
  27. Dotty (versió futurista de Scala) vegeu "Trait parameters"(anglès)
  28. Explicitly Typed Self References(anglès)
  29. "Scala Component Abstractions" (anglès) Vegeu apartat 2.3 Selftype Annotations
  30. scala.Function1(anglès)
  31. scala.Function2(anglès)
  32. "Pattern Matching Anonymous Functions"(anglès)
  33. El tret PartialFunction(anglès)
  34. scala.util.control.Exception(anglès)
  35. Java try-with-resources(anglès)
  36. Loan pattern(anglès)
  37. java.io.Closeable(anglès)
  38. 38,0 38,1 Scala-ARM(anglès)
  39. Assercions(anglès)
  40. Adding ??? to Predef?(anglès)
  41. Is requires keyword is obsolete in Scala 2.8 (or removed)?
  42. Scala Tour - XML(anglès)
  43. A tour of Scala: Views
  44. Pimp my library(anglès)
  45. The "quick pimp" pattern(anglès)
  46. Mètodes amb aritat 0
  47. Views(anglès)
  48. TraversableViewLike, que conté el mètode force.(anglès)
  49. scala.collection.Iterator(anglès)
  50. scala.collection.immutable.Stream(anglès)
  51. La API de Scala (anglès)
  52. Manuals del llenguatge(anglès)
  53. Objecte Predef(anglès) tipus i funcions predefinits.
  54. Hayoo(anglès)
  55. Hoogle(anglès)
  56. Scalex - Hoogle-like documentation search engine for scala library(anglès)
  57. A new API search engine for Scala: scala-search.org(anglès)
  58. Scala Akka(anglès)
  59. ScalaSTM(anglès)
  60. Scala - Akka STM(anglès)
  61. Col·leccions, vista general(anglès)
  62. Scala parallel collections(anglès)
  63. El tret ParIterable(anglès)
  64. parallel.Splitter(anglès)
  65. Scala concurrent package(anglès)
  66. paquet scala.collection - apartat "Creating collections"(anglès)
  67. scala.collection.SeqLike(anglès)
  68. scala.collection.TraversableView(anglès)
  69. scala.collection.TraversableOnce(anglès)
  70. scala.collection.TraversableLike
  71. scala.collection.generic.CanBuildFrom(anglès)
  72. scala.collection.IterableLike
  73. scala.collection.mutable.Buffer(anglès)
  74. scala.collection.Set(anglès)
  75. scala.collection.Map(anglès)
  76. API collection.immutable.StringOps(anglès)
  77. API collection.IndexedSeqLike(anglès)
  78. String interpolation(anglès)
  79. scala.util.matching.Regex(anglès)
  80. Scalaz(anglès)
  81. Learning Scalaz(anglès)
  82. Biblioteca slick de Typesafe(anglès)
  83. Activator de TypeSafe(anglès)
  84. Eclipse (Entorn de desenvolupament)(anglès)
  85. API de la classe JavaTokenParsers(anglès)
  86. API de la classe interna Parsers.Parser(anglès)

Enllaços externs[modifica | modifica el codi]