Wednesday, September 18, 2013

ครบรอบ 1 เดือน กับการใช้ Scala เต็ม ๆ

เนื่องจากนี่เป็นโพสแรก ขอเท้าความหน่อยละกัน ว่าทำไมผมถึงมาลงเอยกับ scala ได้

ถ้าไม่อยากอ่านเท้าความ (นอกเรื่องยาว) ให้ข้ามตัวอักษรสีเทา ๆ ไปครับ ผมพยายามจะทำ spoiler แต่มันไม่ยอม

ตั้งแต่จบใหม่ ๆ ผมอยู่กับ java มาราว ๆ 6-7 ปี ได้ลอง ejb1 (นรก), ejb2 (นรก), struts, spring, hibernate, seam, jsf ฯลฯ ชีวิตก็มีความสุขตามอัตภาพ จนวันนึงได้มาเจอ ruby on rails แล้วก็คิดว่า ลาก่อน java (555) แต่ด้วยงานประจำตอนนั้นใช้ java เลยอด นั่งเขียน ruby กะ python แบบ noob ๆ กะป๋องกะแป๋ง ทำ shell script ใช้เองไปวัน ๆ ทำไรไม่ได้ก็รอ ๆ ๆ จน grails ออก version 1.0 ผมก็เอาไปโม้ให้พี่ ๆ เพื่อน ๆ ฟัง จนเค้ายอมให้ผมใช้ ผมก็ใช้มา 4-5 ปีได้ (ปัจจุบันก็ยังใช้อยู่)

ฟ้ากลั่นแกล้ง.. จากแรก ๆ ที่ happy ดี แต่พอใช้ท่าพิศดารก็เริ่มเจอบั๊กแปลก ๆ มากขึ้นเรื่อย ๆ ไปดู issue tracker ของ grails ก็ละเหี่ยใจ มี bug มากมายที่รอคิวแก้ หลายข้อเป็นบั๊กจริง ๆ ที่คาอยู่หลายปีแต่ยังไม่โดนแก้ จากที่ชอบเพราะความเร็ว ก็มาเจอบั๊กสกัดดาวรุ่งซะ แถมบังเอิญงานหลักที่แก๊งผมทำ มีความจำเป็นต้องแก้ (change) ด่วน ๆ deploy ด่วน ๆ ทุกวัน หรือบางช่วง top form วันละหลายครั้ง การไม่มีเทส (บู่ ...) + deploy ด่วน และภาษาเป็น dynamic ทำให้การพิมพ์ผิดและเขียนอะไรผิดนิด ๆ หน่อย ๆ ที่ compiler ควรจัดการให้ กลายเป็นระเบิดเต็มหน้า user เวลา user ส่ง email หรือโทรมาทีไรก็แสนจะเขิน "แหะ ๆ เดี๋ยวแก้ให้นะค้าบ รอ 10 นาทีค้าบ" ผมก็เลยเริ่มมองหาทางเลือกอื่น โดยมีเงื่อนไข 3 ข้อ.. คือ (1) เป็น static typing ยิ่ง static ยิ่งดี (2) predictable คือ ไม่มี magic เช่น การ add method วิเศษ หรือ การทำอะไรก็ตามที่เราหาที่มาได้ยาก และ (3) ต้องกระชับ โดยข้อ (1) และ (2) ก็เพื่อให้ compiler ช่วยเรามากที่สุดและถ้าเจอบั๊กอะไร เราจะได้แก้โค้ดของ library เองได้ง่าย ๆ ส่วนข้อ (3) ก็เพื่อให้โค้ดของเราอ่านง่าย แก้ง่าย

สวรรค์มีตา.. ระหว่างที่ต่อสู้กับบั๊กของ grails และการแก้ + deploy ด่วนรายวัน ผมได้รู้จัก scala (2.7.5) พอดี แวบแรกก็เฉย ๆ เหมือน toy project ของใครซักคน แต่พอได้ลองเล่น ได้ดูโค้ดที่คนอื่นเขียน เอ้ย เจ๋งเว้ย เป็นภาษา static (กว่า java เยอะ) typing เมพ น่าจะช่วยในสถานการณ์ที่เราไม่ทำ automated regression tests ได้ แถม scala ยังลอกข้อดีภาษาอื่นมาใช้หลายเรื่อง โดยเฉพาะ functional programming แบบ haskell แต่ก็ยังเอามาใช้ไม่ได้ เพราะติดข้อจำกัดหลายเรื่อง โดยเฉพาะ IDE ... อนาถมาก อนาถจริง ๆ แค่เปิด intellij ขึ้นมาก็มี exception แล้ว -..-' highlight ผิด ๆ ถูก ๆ เรียกว่าเชื่อ IDE ไม่ได้เลยดีกว่า (การเขียนภาษาใหม่โดยไม่พึ่ง IDE เลยนี่..​ เหนื่อยไปนะ) ไล่หา implicit conversion (typeclass) ก็ไม่ได้ โหดพอกับ grails magic เลย ก็เลยได้แต่อ่านโน่นอ่านนี่ตามข่าวไปเรื่อย แต่ไม่ได้ลงมือใช้จริงซักเท่าไหร่ รอมันโต

2-3 ปีผ่านไป ตอนนี้ IDE เมพพอแล้ว ecosystem ของ scala ก็แข็งแกร่งมากกว่าแต่ก่อนเยอะ ได้เวลาใช้จริงซักที

... ชิ้ง ฟ้าประทาน project ใหม่มาให้ พร้อมเงื่อนไขต่าง ๆ ที่สามารถตัด grails ทิ้งและเลือก scala (+ lift) เข้ามาได้อย่างปลอดภัยโดย tools และ libraries ที่ผม cen 1 1 no rum na sadddd กับงานนี้คือ

Build tool: SBT 0.12
Programming language: Scala 2.10.2
Web framework: Lift 2.5.1 (stable)
Database access library: Slick 1.0.1
Test framework: Specs2 (ว่าจะใช้ ในอีกเดือนถัดไป)
IDE: (IntelliJ IDEA 12.0.4 + Scala plugin) & (Eclipse + Scala IDE) ตัวเดียวเอาไม่อยู่..

เข้าเรื่องซะที 555 หลังจากเกริ่นซะยาว..

ใน post นี้ ผมขอเล่าคร่าว ๆ ก่อนละกันครับ ว่า scala นี่มันมีข้อดีข้อเสียยังไงเมื่อเทียบกับภาษายอดนิยมอย่าง java, groovy และ haskell (ห๊ะ !?!) ไว้โพสหน้า ถ้าผมยังเขียนอยู่ จะเริ่มเจาะเรื่องที่ว่าเป็นข้อดีและข้อเสียทีละข้อ เพราะโพสหน้า ผมอาจจะกำลังหาทางเลือกอื่นอีกแล้วก็ได้ 555 (หวังว่าไม่นะ)

ซีเรียส ๆ หมัดเด็ดของ scala จริง ๆ เลย คือ
  1. เป็น static typing มัน static กว่า java เยอะ แต่ก็ไม่สุดเหมือน haskell แต่แค่นี้ก็เพียงพอทำให้เรื่องพิมพ์ผิด หรือการแก้ type ตัวแปร แยก class รวม class ถูก compiler จับได้หมด แถมบางทีเวลาเราเขียน handle เงื่อนไขต่าง ๆ ไม่ครบ (ตอน pattern matching) compiler ยังขึ้น warning เตือนเราได้อีก เหลือเรื่อง collection และ equality test ที่ยังสู้ haskell ไม่ได้ แต่ก็มี library ช่วยให้พอกล้อมแกล้มไปได้
    • ผมต้องแก้ structure ใน database mapping layer หลายครั้งมาก แต่ทุกครั้ง compiler จับให้หมด ว่าต้องตามไปแก้ตรงไหนบ้าง ถ้าเป็น groovy คงลำบาก java น่าจะสูสี ส่วน haskell มี newtype keyword ที่ทำให้ type มัน static เข้าไปอีก แต่ผมว่าเราไม่ได้จำเป็นต้องใช้ขนาดนั้น ซึ่งจริง ๆ scala ก็เริ่มลอกมาแล้ว (AnyVal) แต่ยังบั๊ก ๆ อยู่ บางทีก็ compile ไม่ผ่านซะงั้น
    • หลายครั้งที่พอ compile ผ่าน แล้วโปรแกรมให้ผลถูกต้องเลย เพราะ type ช่วยดักข้อผิดพลาดในการวางแผนเขียนได้ระดับนึง โดยเฉพาะ function ที่เกี่ยวกับ recursion และ data structure ที่ซับซ้อน
  2. รองรับ functional programming style คือเขียนแบบ functional (และ imperative) ได้
    • งานเกือบทุกประเภท ถ้าเขียนแบบ functional ได้ จะ clean กว่า imperative มาก และ scala มี language construct ที่ทำให้การเขียนแบบ functional ไหลลื่น โดยที่ java และ groovy ไม่มีเลย แต่ haskell ก็ยังเมพกว่า scala ในความ functional มาก
    • มี data structure บางอย่างทำให้เราหลีกเลี่ยง exception ยอดนิยมใน java เช่น NullPointerException, NoSuchElementException และอะไรทำนองนี้ได้ ผมเขียน scala เฉลี่ยวันละ 6-8 ชั่วโมงตลอด 1 เดือนที่ผ่านมา เจอ exception ตระกูลนี้รวมกันไม่เกิน 3 ครั้ง (scala ใช้ Option, haskell ใช้ Maybe, ส่วน java + groovy ตัวใครตัวมัน)
    • ไม่มี side-effects ทำให้อ่านง่าย เทสง่าย แก้ง่าย function จะมีขนาดเล็กโดยธรรมชาติ (ส่วนใหญ่ function จะยาวไม่เกิน 1 คืบ)
      • อันนี้ผมคิดเองว่า (ยังไม่ได้ลอง) น่าจะหมดปัญหาเรื่อง TDD + major changes = waste โดยการรอเขียนเทสหลังจากที่ระบบนิ่งระดับนึงแล้ว ... ถ้าจะล่มผมว่าน่าจะล่มที่มันไม่มีความเป็นวินัยมากกว่าเทคนิค
    • ทางทฤษฎี.. เราจะทำ concurrency ง่ายขึ้นและคาดเดาได้ง่ายขึ้น (ยังไม่มีโอกาสได้ทำ)
  3. รันอยู่บน JVM
    • ซึ่งเร็วและแข็งแกร่งมาก
    • ใช้ lib ของ java ได้ ผมใช้ Quartz และ Axis2 เพราะหาอะไรที่ดีกว่าในโลก scala ไม่ได้ ซึ่งก็ใช้ได้เนียนดี เขียน glue code นิดหน่อยตรง axis 2 ให้จัดการเปลี่ยน nullable property -> option และเปลี่ยน class + getter + setter -> case class เพื่อความสวยงาม
    • system engineer ของลูกค้าดูแลได้ เพราะเอาไปรันบน app server ที่เค้าคุ้นเคย เรา build เป็น .jar .war ได้เลย
  4. โค้ดกระชับกว่า java เยอะมาก (และน่าจะภาษา static อื่น ๆ ด้วย) ส่วนนึงเพราะตัวภาษา เช่น lambda function ที่ส่งไปส่งมาได้ ไม่ต้องประกาศ anonymous class และยังมี type inference + implicits (typeclass) ทำให้ลดการประกาศ type ไปได้เยอะ เรียกว่ากระชับใกล้เคียงภาษา dynamic อย่าง groovy แต่ type inference ติดปัญหาที่ต้องรองรับ OO ด้วย ทำให้มันเมพสู้ haskell ไม่ได้ และ noise เยอะกว่าพอสมควร
  5. มี stack trace .. java กับ groovy ก็มี แต่ haskell ไม่มี  *0* ถือว่าเป็น deal breaker ของ haskell อีกข้อ
  6. สุดท้าย ความเห็นส่วนตัว... scala เปิดโลกการเรียนรู้ โลก functional programming มีแนวคิดเด็ด ๆ เยอะมาก การไล่โค้ดของ framework และ library แต่ละครั้งได้เจออะไรดี ๆ มากมาย แบบที่ java + groovy ไม่มี เพราะ java มันมีแค่นั้น ส่วน haskell ก็ยังเมพสุดอยู่ดี ทั้งเรื่องแนวคิดและเรื่องความสวยงาม เพราะมันเป็น functional เนื้อ ๆ ไม่มี OO มาเจือปนเหมือน scala
ข้อเสียกันบ้าง
  1. Document ของตัว scala ถือว่าเยอะพอ แต่ของ library ที่ใช้ ถือว่าน้อยมาก เจอปัญหาอะไรต้องขุด source ตลอด ในที่นี้คือ Lift + Slick น่าจะเป็นปัญหาสำหรับโปรแกรมเมอร์ที่โดนบังคับใช้ เพราะไม่มีแรงใจ.. แต่ถ้าใจรักน่าจะชอบ เพราะขุดแล้วคุ้ม ขุดทีเดียวรู้ที่มาที่ไปทั้งหมด
  2. ระบบ Type ซับซ้อน ยิ่งเจอ framework ที่ใช้ monad + typeclass เยอะ ๆ อย่าง slick นี่ อ่าน doc ยังไงก็ไม่ช่วย ต้องใช้ IDE ช่วยหา implicit conversion (typeclass) และตามไปอ่านโค้ดสถานเดียว ก็เหมือนข้อ 1 คือเสียเวลาอ่านทีเดียวก็จะเข้าใจ และใช้ได้แบบพริ้ว ๆ
    • คนส่วนใหญ่จะไม่ต้องเจออะไร advanced ๆ แบบนี้ ยกเว้นคนที่จะเขียน framework และพวกบุกเบิกเรียนรู้ library ไปสอนคนอื่นเท่านั้น
    • แต่จริง ๆ ติดอะไรก็ไปโพสถามใน mailing list ก็ได้ ผมเคยไปโพสอยู่สองสามครั้ง พวก committer เค้าก็ตอบกันเร็วดี
  3. Compiler มีบั๊ก มันไม่ได้เยอะจนทำงานไม่ได้หรือผิดแบบไม่รู้เนื้อรู้ตัว จะออกแนว compile ไม่ผ่าน ต้อง clean ก่อนถึงจะผ่าน อะไรทำนองนี้ ส่วนมากจะเป็นกับ feature ใหม่ของภาษา หรือบาง class ที่ type มัน complex มาก ๆ .. ก็ทำไรไม่ได้ นอกจาก workaround กันไป
  4. Compile ช้ามาก ช้ากว่า java, groovy และ haskell เรื่องนี้พอจะมี workaround คือลดการใช้ trait และ mixin แต่ผมว่าช้าแต่สวยดีกว่า (ถ้าจำไม่ผิด รู้สึก linkedin จะทำ เพราะโค้ดเค้าใหญ่มาก)
  5. ถึก.. ด้วยความที่ scala community มักไม่นิยมอะไรที่เป็น magic ทำให้ไม่ค่อยมีอะไรวิเศษ ๆ ช่วย (เช่น Grails GORM) ส่วนมากต้องเขียนเอง ทำให้ไม่ค่อยเหมาะกับงาน quick and dirty
  6. ไม่ opinionated เลย งาน ๆ นึงมีวิธีทำได้หลายวิธี ถ้าไม่คุยกันดี ๆ ในทีม มีเฮแน่นอน
เอ๊ะ.. ทำไมเขียนแล้วมันเยอะเท่ากับข้อดี ไม่เป็นไร ถือว่าข้อดีเรารวมหลาย ๆ อันเป็นข้อเดียวละกัน 555

สรุป เดือนแรก ผมเสียเวลากับการขุด framework 2 ตัวคือ Lift กับ Slick ไป น่าจะเกินครึ่งของเวลาที่ใช้ แต่รู้สึกว่าคุ้มมาก เพราะส่วนที่ขุดไปเจอก็น่าจะเป็นกรณีส่วนใหญ่ที่งานทั่วไปต้องใช้ และเริ่มรู้สึกว่าทำได้เร็วขึ้นเรื่อย ๆ อีกส่วนที่เสียเวลาคือโค้ดประเภท helper (จากการไม่มี magic) แต่พอขุดโค้ด framework ไปเรื่อย ๆ ก็เจอว่า framework เค้ามีให้อยู่แล้ว แถมครบกว่าที่เราทำซะอีก -..-'

แล้วคุณล่ะ.. ซักหน่อยมะ ?

ปล: งานนี้ยังไม่ test first เพราะอยากทดสอบ test later ดูว่าจะรอดมั้ย

No comments:

Post a Comment