0Pricing
Scala for Backend Engineering & Functional Programming · درس

Source وFlow وSink

لبنات بناء التدفق

Source وFlow وSink درس مجاني في Scala for Backend Engineering & Functional Programming على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Scala for Backend Engineering & Functional Programming، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Scala for Backend Engineering & Functional Programming 4 دروس في المجموع.

بعض أجزاء هذا الدرس لم تُترجم بعد وتظهر باللغة الإنجليزية.

The Three Building Blocks

Akka Streams models a data pipeline as a graph of processing stages. The three core linear stages are Source (produces elements), Flow (transforms them), and Sink (consumes them).

A Source has one output, a Sink has one input, and a Flow has exactly one input and one output. Wiring them together describes what should happen, not when.

Defining a Source

A Source[Out, Mat] emits elements of type Out and exposes a materialized value of type Mat. The simplest sources come from in-memory collections or ranges.

Until the stream is run, a Source is just an immutable blueprint that can be reused freely.

import akka.stream.scaladsl.Source

val numbers: Source[Int, akka.NotUsed] =
  Source(1 to 100)

val single: Source[String, akka.NotUsed] =
  Source.single("hello")

Defining a Sink

A Sink[In, Mat] consumes elements of type In. The materialized value often captures the result of consumption, such as a Future that completes when the stream finishes.

Sink.foreach runs a side effect per element; Sink.fold accumulates a single result.

import akka.stream.scaladsl.Sink
import scala.concurrent.Future

val printSink: Sink[Int, Future[akka.Done]] =
  Sink.foreach(println)

val sumSink: Sink[Int, Future[Int]] =
  Sink.fold(0)(_ + _)

Defining a Flow

A Flow[In, Out, Mat] sits between a Source and a Sink, transforming each element. Flows are reusable on their own and can be composed before being attached to any endpoint.

Here a Flow doubles integers and converts them to strings.

import akka.stream.scaladsl.Flow

val doubleToString: Flow[Int, String, akka.NotUsed] =
  Flow[Int]
    .map(_ * 2)
    .map(n => s"value=$n")

Connecting Source to Sink

The via operator attaches a Flow to a Source, and to attaches a Sink. Connecting a Source directly to a Sink with to produces a RunnableGraph: a closed, runnable blueprint.

No elements move yet; this only describes the topology.

import akka.stream.scaladsl.{Source, Sink, RunnableGraph}

val graph: RunnableGraph[akka.NotUsed] =
  Source(1 to 10).to(Sink.foreach(println))

via: Inserting a Flow

Use via to splice a Flow into the pipeline. A Source.via(flow) yields a new Source whose output type matches the Flow's output.

Chaining via calls lets you build long transformation pipelines from small, testable Flow pieces.

val pipeline =
  Source(1 to 10)
    .via(Flow[Int].filter(_ % 2 == 0))
    .via(Flow[Int].map(_ * 10))
    .to(Sink.foreach(println))

Type Safety Across Stages

The compiler enforces that the output type of each stage matches the input type of the next. A Source[Int] cannot connect to a Sink[String] without an intervening Flow that converts the type.

This static checking catches pipeline wiring mistakes before runtime.

// Source[Int] -> Flow[Int, String] -> Sink[String]
val ok =
  Source(1 to 3)
    .via(Flow[Int].map(_.toString))
    .to(Sink.foreach[String](println))

Materialized Values

Each blueprint carries a materialized value: a handle produced when the stream runs. A Sink.fold materializes a Future of the result. By default, combining stages keeps the leftmost materialized value (NotUsed for plain sources).

Use toMat and Keep to select which side's value you want.

import akka.stream.scaladsl.Keep
import scala.concurrent.Future

val g: RunnableGraph[Future[Int]] =
  Source(1 to 100)
    .toMat(Sink.fold(0)(_ + _))(Keep.right)

Reusable Components

Because Sources, Flows, and Sinks are immutable values, you can define them once and reuse them across many pipelines. This encourages a library of small, named processing stages.

A Flow defined for parsing can be inserted into both a file pipeline and an HTTP pipeline.

val parse: Flow[String, Int, akka.NotUsed] =
  Flow[String].map(_.trim.toInt)

val fromFile  = lines.via(parse)
val fromHttp  = requestBody.via(parse)

Common Source Constructors

Akka Streams ships many Source factories: Source.single, Source.repeat, Source.tick for timed emission, Source.future from a Future, and Source.empty.

Choosing the right constructor makes the intent of a data producer explicit.

import scala.concurrent.duration._

val ticks  = Source.tick(0.seconds, 1.second, "tick")
val onceF  = Source.future(scala.concurrent.Future.successful(42))
val forever = Source.repeat("x")

Common Sink Constructors

Likewise, sinks include Sink.head (first element as a Future), Sink.seq (collect all into a Seq), Sink.ignore (drain and discard), and Sink.last.

For pipelines that feed a result back to your code, Sink.seq and Sink.fold are the most common.

import scala.concurrent.Future

val collect: Sink[Int, Future[Seq[Int]]] = Sink.seq
val firstOne: Sink[Int, Future[Int]]    = Sink.head
val drain: Sink[Int, Future[akka.Done]] = Sink.ignore

Quick Check

Test your understanding of the linear stage types.

Recap

You learned the three linear building blocks: Source produces, Flow transforms, and Sink consumes. They are immutable, reusable blueprints connected with via and to.

Connecting a Source to a Sink yields a RunnableGraph that carries a materialized value but does not move any data until run. Next you will transform streams with richer operators.

الأسئلة الشائعة

هل درس «Source وFlow وSink» مجاني؟

نعم — نص درس «Source وFlow وSink» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Scala for Backend Engineering & Functional Programming، انتقل إلى CoddyKit PRO. تتضمن دورة Scala for Backend Engineering & Functional Programming 4 دروس في المجموع.

ماذا ستتعلم في «Source وFlow وSink»؟

لبنات بناء التدفق تتمرن على Scala for Backend Engineering & Functional Programming مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Scala for Backend Engineering & Functional Programming؟

لا تُشترط خبرة سابقة. Scala for Backend Engineering & Functional Programming على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.

كم من الوقت يستغرق درس «Source وFlow وSink»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Scala for Backend Engineering & Functional Programming هذا؟

نعم. كل درس في Scala for Backend Engineering & Functional Programming يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. Source وFlow وSink
  2. تحويل التدفقات
  3. التحكم في الضغط
  4. تشغيل خط أنابيب
← العودة إلى Scala for Backend Engineering & Functional Programming