0Pricing
Scala for Backend Engineering & Functional Programming · Lezione

Desugaring di for

map e flatMap

Desugaring di for è una lezione Scala for Backend Engineering & Functional Programming gratuita su CoddyKit. Questa è la lezione 1 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Scala for Backend Engineering & Functional Programming, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Scala for Backend Engineering & Functional Programming include 4 lezioni in totale.

Che cos'è una for-comprehension?

Una for-comprehension in Scala è una sintassi speciale per lavorare con collezioni e altri tipi contenitore. Sembra un ciclo imperativo, ma in realtà è zucchero sintattico che il compilatore riscrive in chiamate a map, flatMap, filter e foreach.

Capire il desugaring aiuta a ragionare su qualsiasi for-comprehension, anche su tipi personalizzati.

object Main {
  def main(args: Array[String]): Unit = {
    val result = for (x <- List(1, 2, 3)) yield x * 10
    println(result)
  }
}

Un singolo generatore diventa map

Quando una for-comprehension ha esattamente un generatore e usa yield, il compilatore la riscrive in una singola chiamata a map.

  • for (x <- xs) yield f(x)
  • diventa xs.map(x => f(x))
object Main {
  def main(args: Array[String]): Unit = {
    val a = for (x <- List(1, 2, 3)) yield x + 1
    val b = List(1, 2, 3).map(x => x + 1)
    println(a == b)
  }
}

La parola chiave yield

La parola chiave yield indica a Scala di raccogliere i risultati in una nuova collezione dello stesso tipo.

Senza yield, la for-comprehension esegue un effetto collaterale per ogni elemento e restituisce Unit (diventa foreach).

object Main {
  def main(args: Array[String]): Unit = {
    // No yield: side effect only
    for (x <- List("a", "b")) println(x)
    // With yield: builds a new List
    val upper = for (x <- List("a", "b")) yield x.toUpperCase
    println(upper)
  }
}

Due generatori diventano flatMap + map

Con due o più generatori, quelli esterni diventano flatMap e quello più interno diventa map.

  • for (x <- xs; y <- ys) yield f(x, y)
  • diventa xs.flatMap(x => ys.map(y => f(x, y)))
object Main {
  def main(args: Array[String]): Unit = {
    val pairs = for {
      x <- List(1, 2)
      y <- List("a", "b")
    } yield (x, y)
    println(pairs)
  }
}

Vedere l'equivalente con flatMap

Qui scriviamo la stessa logica in entrambi i modi. Dimostrare che sono equivalenti mostra che le for-comprehension non aggiungono magia, ma solo leggibilità.

object Main {
  def main(args: Array[String]): Unit = {
    val sugar = for {
      x <- List(1, 2)
      y <- List(10, 20)
    } yield x + y

    val desugared = List(1, 2).flatMap(x => List(10, 20).map(y => x + y))

    println(sugar)
    println(sugar == desugared)
  }
}

Perché flatMap per il generatore esterno

Ogni elemento esterno produce un'intera collezione di risultati interni. Se usassimo map per il generatore esterno, otterremmo una collezione annidata come List(List(...), List(...)).

flatMap appiattisce queste liste annidate in un unico risultato piatto.

object Main {
  def main(args: Array[String]): Unit = {
    val nested = List(1, 2).map(x => List(10, 20).map(y => x + y))
    val flat   = List(1, 2).flatMap(x => List(10, 20).map(y => x + y))
    println(nested)
    println(flat)
  }
}

for senza yield diventa foreach

Un ciclo for senza yield viene riscritto in foreach. Esegue il corpo per i suoi effetti collaterali e produce Unit.

  • for (x <- xs) doSomething(x)
  • diventa xs.foreach(x => doSomething(x))
object Main {
  def main(args: Array[String]): Unit = {
    val r: Unit = for (x <- List(1, 2, 3)) print(x + " ")
    println()
    println("Return type is Unit")
  }
}

Il tipo del risultato segue il primo generatore

Il tipo del risultato è determinato dalla collezione del primo generatore. Un for su una List produce una List; su un Set, un Set; su un Option, un Option.

object Main {
  def main(args: Array[String]): Unit = {
    val fromList = for (x <- List(1, 2, 2, 3)) yield x
    val fromSet  = for (x <- Set(1, 2, 2, 3)) yield x
    println(fromList)
    println(fromSet)
  }
}

Associare valori con =

All'interno di una for-comprehension è possibile associare valori intermedi con =. Questo evita di ricalcolare i valori e migliora la leggibilità.

Viene desugared in un map che trasporta il valore aggiuntivo all'interno di una tupla.

object Main {
  def main(args: Array[String]): Unit = {
    val result = for {
      x <- List(1, 2, 3)
      doubled = x * 2
    } yield doubled + 1
    println(result)
  }
}

Funziona con qualsiasi tipo dotato di map/flatMap

Poiché il desugaring si basa solo su map e flatMap, le for-comprehension funzionano con qualsiasi tipo che definisca questi metodi: Option, Either, Try, Future e le Sue classi.

object Main {
  def main(args: Array[String]): Unit = {
    val combined = for {
      a <- Some(2)
      b <- Some(3)
    } yield a * b
    println(combined)
  }
}

Il vero vantaggio è la leggibilità

Confronti una catena profondamente annidata di flatMap/map con una for-comprehension chiara. Vengono compilate nello stesso modo, ma la versione con for si legge dall'alto verso il basso come una ricetta.

object Main {
  def main(args: Array[String]): Unit = {
    val chained = List(1, 2).flatMap(a => List(3, 4).map(b => a * b))
    val readable = for {
      a <- List(1, 2)
      b <- List(3, 4)
    } yield a * b
    println(chained == readable)
  }
}

Verifica rapida

Come desugared dal compilatore una for-comprehension con due generatori e un yield?

Riepilogo

Ha imparato come vengono desugared le for-comprehension:

  • Un generatore + yield → map
  • Più generatori + yield → flatMap (esterno) + map (interno)
  • Nessun yield → foreach che restituisce Unit
  • Le associazioni val = aggiungono valori intermedi
  • Il tipo del risultato segue il primo generatore

Qualsiasi tipo dotato di map e flatMap può essere usato in una for-comprehension.

Domande Frequenti

La lezione «Desugaring di for» è gratuita?

Sì — il testo completo di «Desugaring di for» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Scala for Backend Engineering & Functional Programming, passa a CoddyKit PRO. Il corso Scala for Backend Engineering & Functional Programming include 4 lezioni in totale.

Cosa imparerò in «Desugaring di for»?

map e flatMap Eserciti Scala for Backend Engineering & Functional Programming con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Scala for Backend Engineering & Functional Programming?

Non è richiesta alcuna esperienza precedente. Scala for Backend Engineering & Functional Programming su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 1 di 4.

Quanto tempo richiede la lezione «Desugaring di for»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Scala for Backend Engineering & Functional Programming?

Sì. Ogni lezione Scala for Backend Engineering & Functional Programming include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Desugaring di for
  2. Filtrare con if
  3. Generatori multipli
  4. Usare Option e Future
← Torna a Scala for Backend Engineering & Functional Programming