ทำการ Refactor โค้ด Scala ด้วย High-order function
บทความนี้ได้แรงบันดาลใจจากเพื่อน ThoughtWorks ที่เคยเป็น partner กับบริษัทผม โดยเค้าเสนอให้ลองใช้ high-order function ในการเขียน Scala ซึ่งเป็น functional programming language อยู่ละ
ตัวอย่างโค้ด
เป็นตัวอย่างโค้ดของ Spark streaming application มาในรูปแบบ Input Stream => Streaming Computation => Output Sink
คำถามตอนนั้นคือ เราจะปรับปรุง code นี้ให้ดีขึ้นไปอีกยังไง
ว่าด้วยเรื่องของ Functional programming
รูปแบบ code ลักษณะนี้ที่เจอคือการเขียนแบบ Imperative style ซึ่งจะเน้นเป็นการอธิบายขั้นตอนการทำงานไปเรื่อยๆ แต่ปัญหาที่เกิดคือ ถ้าในอนาคตมันมี computation มากขึ้นไปเรื่อยๆ แปลว่าขั้นตอนมีมากขึ้น การจัดการ ดูแลรักษาเริ่มยากขึ้น เลยนำ Object-Oriented Programming (OOP) มาใช้ (Scala ก็เขียน OOP ได้นะครับ) ซึ่งก็ช่วยจัดกลุ่ม behavior ที่เกี่ยวข้องกันได้อยู่ แต่การเขียนใน Streaming app ก็เป็นแบบเดิมอยู่ดี
ถ้าเราเปลี่ยนจากการเขียนแบบนี้
val input = fromInputStream
val df1 = compute
val df2 = computeMore
val output = toOutputSink
เป็นแบบนี้หละ
fromInputStream andThen
compute andThen
computeMore andThen
toOutputSink
เริ่มใช้ High-order function ใน Scala
อธิบายง่ายๆ คือ function ที่ return function อีกที เช่น function map
ใน salaries.map(x => x * 2)
หมายความว่ามัน return function ซึ่งนำ x ไปคูณ 2 อีกที
ส่วนการสร้าง SparkSession
เป็นส่วนแรก return function ที่ไม่รับอะไรเลย (Void) ที่ return SparkSession
อีกที
ส่วนการทำ Input-Output
ฝั่ง Input รับต่อมาจากการสร้าง SparkSession
ซึ่ง return DataFrame
อีกที
ฝั่ง Output รับ DataFrame
มาจากการทำ computation เสร็จ แล้วก็ return StreamingQuery
อีกที
ส่วนการทำ computation
ตรงนี้ก็ return function ที่รับ DataFrame
มาจาก Input stream ที่ return DataFrame
อีกที
เอามารวมกันจะได้หน้าตาประมาณนี้
ทำการร้อย function เข้าด้วยกัน (compose) ผ่าน andThen
โดย function ถือ reference ของอีก function นึงไปเรื่อยๆ จนกว่าจะจบ flow
จริงๆ มันก็เหมือนกับการเขียนแบบนี้ แต่อ่านง่ายกว่าเยอะ ฮ่าๆๆ
การแก้ชุดการทดสอบก็ไม่ใช่เรื่องใหญ่อะไร
เป็นอีกแนวทางนึงในการอธิบาย code ของเราว่าทำอะไร (what) มากกว่าทำยังไง (how) ครับ
ตัวอย่างโค้ด https://github.com/raksit31667/example-spark-gradle