Tag Archives: Functional Programming

How Clojure teaches me about side-effects

ดัดแปลงมาจากคอมเมนต์นี้ https://www.facebook.com/groups/919377878100706/permalink/1121787907859701/?comment_id=1121803797858112&reply_comment_id=1121826181189207

หลังจากที่ผมโพสว่า Clojure สอนผม ว่าผมจะอยู่ร่วมกับ side-effects ได้อย่างไร

พี่มาคอมเมนต์ Patraphong Sukhonpitumart ถามว่า

clojure สอนยังไง มีตัวอย่างมั้ยครับ

ผมเลยตอบว่า

ที่ผมพอจะนึกออกตอนนี้ ไม่ได้ exclusive สำหรับ clojure นะครับ

  • สอนให้ model domain เป็น value ไม่ใช่ object มันเหมาะกับแนวคิด bounderies (Functional Core, Imperative Shell) มาก
  • แยกที่เก็บ mutable data (atom) ให้ชัดเจน เราจะเรียกใช้แก้ไขมันเมื่อไหร่ก็ได้ แต่ก็จะบอกว่าพยายามให้มีให้น้อยที่สุด
  • ไม่มีคนทำ ORM แล้วประสบความสำเร็จ
  • Referential transparency จะใช้ไม่ได้ถ้ามี side-effects ฟังก์ชันอย่าง memoize ก็จะทำงานผิด ถ้าไม่สนใจเรื่องนี้
  • ความที่เราสามารถทำ side-effect ในฟังก์ชันได้ตามใจ แต่เค้าก็จะบอกว่าไม่ควรทำ เช่น ฟังก์ชันที่ใช้ update ค่าให้ atom (เพราะมันไม่การันตีว่าจะถูกเรียกแค่ครั้งเดียว)
  • ไม่สร้างความยุ่งยากให้เราหากเราจะทำ side-effects ง่ายๆ เช่น เราอยากจะ println เมื่อไหร่ก็ได้ ไม่โดน type บังคับ
  • พวก React wrapper ก็จะบอกให้เราระวังเรื่อง state โดยที่บางตัวก็ strict บางตัวก็ปล่อยหลวมๆ
  • def เป็น mutable เพราะมันไม่จำเป็นต้อง immutable
  • do, doseq ทำให้เรารู้ว่าเราทำ side-effects อยู่นะคือ ผมมีโอกาสเลือกอยู่เสมอว่าผมจะทำ side-effects ได้ แต่ผมก็จะเห็นชัดเจนว่าถ้าผมทำแล้วปัญหาที่ตามมาคืออะไร

Blending functional programming with imperative. Higher-level of abstraction or functional core.

ดัดแปลงและเพิ่มเติมจากคอมเมนต์คำถามในโพสนี้ https://www.facebook.com/nuttanart/posts/1015437440718608 เป็นคำถามจาก Supanat IBoss Potiwarakorn

ผมได้ยินคนพูดถึง functional core, imperative shell มาซักพักและ แต่ผมก็มักจะได้ยินว่า functional เนี่ยเป็น higher level abstraction เราแอบเอาลำดับขั้นตอนการทำงานต่างๆ ไปแอบไว้ภายใต้ declarative code ผมก็เลยยังรู้สึกสับสนอยู่เล็กน้อย (รู้สึกว่าเห้ย ถ้าเป็น shell มันต้องอยู่บนกว่าสิ)

เท่าที่ผมเข้าใจคือ monad เนี่ยมันคือเหมือน imperative shell ซึ่งจริงๆ แล้วมันก็คือ เรา แอบ state mutation เอาไว้ ไอสองเรื่องที่ผมพูดอ้างถึงบนนี่มันไม่ได้ขัดแย้งกัน แต่มันแค่อธิบายในคนละมุมกันถูกมั้ยฮะ

คำตอบของผม

จริงๆ เราคิดอะไรคล้ายๆ กันนะเนี้ย ผมก็เคยสงสัยเรื่องเดียวกันนี้เลย จำไม่ได้แล้วว่าตอนนั้นตอบตัวเองว่ายังไง แต่ถ้าพยายามตอบคำถามนี้ด้วยความเข้าใจในปัจจุบันจะตอบแบบนี้

ตอนนี้มีสองเรื่อง
a) functional เป็น declarative (higher-level abstraction) อยู่ด้านบน ส่วน imperative (lower-level abstraction) อยู่ด้านล่าง อย่างเช่นเราเขียน map,reduce โดยไม่ต้องวนลูปเอง
b) imperative อยู่ด้านบน functional อยู่ด้านล่าง ตามแนวคิด Functional Core, Imperative Shell เช่น เอา UI, http request, database ไว้ด้านบน หรืออย่าง Actor  model

เวลาเราเขียนโปรแกรมจริงๆ เราจะไม่ได้คำนึงถึงข้อ a) อะเพราะไม่ใช่หน้าที่เราจะต้องไปสนใจการทำงานตรงนั้น platform มีหน้าที่การันตีให้เรา ด้านล่างของเรามันไม่ functional อยู่แล้ว แต่ platform ทำให้เรารู้สึกว่าเราควบคุมทุกอย่างได้ในลักษณะ functional (input – output) แต่สำหรับ b) เราต้องสนใจเพราะเป็นสิ่งที่เราต้องเป็นคนสั่งให้มันทำ เพราะฉะนั้นแล้วในชั้นของเราที่ใช้ภาษาเขียนเพื่อแก้ปัญหาอื่นๆ เราคงโฟกัสที่ b) มากกว่า ส่วน a) สำหรับเราน่าจะสำคัญแค่ตอนที่เราอธิบายให้คนอื่นฟังว่า functional มันทำงานยังไงแค่นั้นมั้ง

จริงๆ แล้วเราอาจจะต้องไปเขียน imperative ด้านล่างบ้างก็ได้ แต่เราก็ควรห่อให้ imperative นั้นเรียบร้อยอยู่ภายในแล้ว expose ออกมาเป็นแค่ functional ให้ด้านบนใช้ ผมเชื่อว่าการที่ด้านล่างของเราเรียบง่ายและตรงไปตรงมาจะทำให้เราสามารถเข้าใจระบบโดยรวมของเราได้ง่ายกว่า จะเพิ่มฟีเจอร์ก็มั่นใจ จะ debug ก็ไล่ได้ง่าย

ผมชอบประโยคนี้ของ José Valim (ผู้สร้าง Elixir)

I often summarize functional programming as a paradigm that forces us to make the complex parts of our system explicit

ผมคิดว่าการที่เราเอา imperative ซึ่งเป็นส่วนที่ยากขึ้นมาไว้ด้านบน (ไม่ซ่อนอยู่ด้านล่าง) เป็นตัวอย่างที่ดีมากของการทำให้ complex parts explicit

ส่วนเรื่อง monad นี่ไม่รู้เลยอะ(เขียน clojure ไม่ต้องรู้จัก monad 😜) แต่ก็เคยได้ยินมาบ้างเหมือนกันที่ว่ามองมันเป็น imperative shell ได้

เรื่องสำคัญที่หนังสือ OOP ทั่วไปไม่ได้สอน

ดัดแปลงจาก Facebook โพสนี้ https://www.facebook.com/nuttanart/posts/10154374407186081

ซึ่งเป็นโพสต่อเนื่องจากโพสแนะนำหนังสือ 99 Bottles of OOP อันนี้ https://www.facebook.com/nuttanart/posts/10154374324641081

ถึงแม้ผมจะเชียร์หนังสือ 99 Bottles of OOP อย่างไร ผมยังรู้สึกว่ายังมีปัญหาสำคัญมากๆ อันหนึ่งที่ทำให้โค้ดเราเน่าและหนังสือเล่มนี้คงจะไม่ได้พูดถึง หนังสือเล่มอื่นๆที่ผมเคยได้อ่านมาก็ไม่ได้สอน คือ การอยู่ร่วมกันระหว่าง OO กับสิ่งเหล่านี้
– การเชื่อมต่อ database
– การทำ external call
– logging
– อ่าน/เขียนไฟล์
– UI
– ฯลฯ

อาจจะมีแนวคิด Clean Architecture ที่พูดถึงอยู่บ้าง เพียงแต่ว่างบางครั้งเราไปโฟกัสที่ตัว pattern และแต่ละ component มากเกินไปจนลืมว่าจริงๆ แล้วต้นเหตุของปัญหาของเราคืออะไร

ผมคิดว่าตราบใดที่สิ่งเหล่านี้ยังปะปนกันอยู่ในโค้ดของเราอยู่ เราจะรู้สึกว่าสิ่งที่หนังสือสอนเขียนโค้ดดีๆ เค้าสอนเนี้ย มันทำไม่เห็นจะทำได้จริงเลย

บางคนอาจจะคิดในใจว่า นี่มัน side-effects นี่หว่า ใช่แล้วครับ ผมคิดว่าอีกปัจจัยหนึ่งที่จะทำให้เราเขียนโค้ด OO ได้ดี ดันเป็นสิ่งที่ OO ไม่ได้สอน แต่มันอยู่ในแนวคิด functional programming ถ้ารู้ว่าจะจัดสิ่งเหล่านี้ให้อยู่ถูกที่ถูกทางได้อย่างไร มันจะทำให้เราจะมีพื้นที่ให้จัดโค้ด OO ให้ดีได้

ลองไปศึกษาเรื่อง side-effects ดูนะครับ แล้วจะพบว่าหลายๆ ปัญหาที่เราเจอกันอยู่ สาเหตุเกิดจากพวกมันนี่แหละ

Update: talk นี้อธิบายเพิ่มเติมได้ดีว่า side-effects คืออะไร และมันสำคัญอย่างไร

closure, FP และ OOP

เมื่อวานนี้พูดถึงคำนิยามของ closure ไป วันนี้ผมจะนำมันมาใช้สร้างโค้ดที่มีลักษณะคล้ายโค้ดจากภาษา Object-oriented programming (OOP) ให้ดูครับ

เริ่มต้นด้วยโค้ด OOP บน Ruby ก่อน

class Prepend
  def initialize(start_word)
    @start_word = start_word
  end

  def prepend_for(end_word)
    @start_word + " " + end_word  # Not idiomatic Ruby but should be more familiar for other languages user
  end
end

hello = Prepend.new("Hello")
hello.prepend_for("Tap") # => "Hello Tap"
hello.prepend_for("OOP") # => "Hello OOP"

ผมทำสร้างคลาส Prepend ซึ่งมี constructor method ที่รับพารามิเตอร์ 1 ตัวคือคำแรก (start_word) เก็บไว้เป็น instance variable และสร้างเมท็อด prepend_for ที่รับพารามิเตอร์หนึ่งตัวคือคำท้าย (end_word) นำคำแรกและคำท้ายต่อกัน

Continue reading

Lambda กับ Closure ต่างกันยังไง

ตั้งแต่แรกที่ผมได้ยินเกี่ยวกับเรื่อง Functional programming ผมก็ได้ยิน 2 คำนี้มาตลอด แต่ไม่เคยรู้ซักทีว่าเมื่อไหร่ควรใช้คำใด เมื่อไหร่เราควรเรียกว่า Lambda เมื่อไหร่เราควรเรียกว่า Closure พอวันนี้รู้แล้วก็เลยมาเล่าให้ฟังครับ ขออธิบายแบบไม่ทางการนะครับ

Lambda คือการประกาศฟังก์ชันแบบไม่มีชื่อ โดยการประกาศนี้บอกแค่เพียงว่าฟังก์ชันนี้รับพารามิเตอร์อะไรบ้าง และทำกระบวนการอะไรภายในนั้นกับพารามิเตอร์เหล่านั้น

ตัวอย่างเช่น

function(x,y) {
  return x+y;
}

ซึ่งเจ้า Lambda function นี้อาจจะถูกเรียกใช้ทันที

(function(x,y) {
  return x+y;
})(1,2) // => 3

จะถูกเก็บไว้ในตัวแปรเพื่อเรียกใช้ทีหลัง

var sum = function(x,y) {
  return x+y;
}
sum(1,2)

หรือจะถูกส่งเป็นอาร์กิวเมนต์เข้าไปให้ฟังก์ชันอื่นก็ได้

Continue reading

ขี้เกียจเขียน For loop ภาค 3 – ของจริง!

อารมณ์ยังค้างไม่หายจากภาค 2 ที่เขียนไปเมื่อวานนี้ วันนี้ขอเขียนอย่างต่อเนื่องอีกซักบล๊อกนึง เช่นเคยใครยังไม่ได้อ่านตอนที่แล้ว เชิญติดตามได้ที่ ขี้เกียจเขียน For loop ภาค 2 ครับ

วันนี้ยังคงอยู่กันที่โจทย์การแข่งขันเขียนโปรแกรม คราวนี้จะเจาะจงลงไปที่โจทย์แข่งจริงๆ เลย โดยโจทย์นี้เอามาจากการแข่งขัน Facebook Hacker Cup 2013 รอบคัดเลือก ที่พึ่งจะจบไปหมาดๆ เมื่อสุดสัปดาห์ที่ผ่านมา โดยผมทำได้เพียงข้อเดียวคือข้อแรก ที่แทบจะเรียกได้ว่า สำหรับคนที่มีอาชีพเป็นโปรแกรมเมอร์แล้วควรจะทำข้อนี้ได้ เพราะความยากอยู่ในระดับที่เราพบเจอได้ในโจทย์เขียนโปรแกรมในการทำงานประจำวันบ่อยครั้ง

แต่โดยส่วนตัวมีความประทับใจกับการแก้โจทย์ข้อนี้ได้เป็นอย่างมาก เพราะเป็นครั้งแรกที่สามารถแก้ปัญหาแข่งขัน โดยไม่มีการ mutate ค่าของตัวแปร ได้สำเร็จเป็นครั้งแรก ถือเป็นหนึ่งใน achievement จากการที่นั่งขุดนั่งเล่น functional programming concept มาซักพัก

โจทย์เต็มๆ อยู่ที่นี่ Beautiful strings ไม่แน่ใจนะครับว่าคนที่ไม่ได้เข้าร่วมการแข่งขัน จะสามารถเข้าไปดูได้หรือเปล่า

โจทย์โดยสรุปมีอยู่ว่า เค้าให้ string ที่มีตัวอักษร a-z ปนตัวใหญ่ตัวเล็กแก่เรามา ให้ assign เลข 1 – 26 ให้แก่ตัวอักษรแต่ละตัว แบบไม่สนใจตัวใหญ่ตัวเล็ก ให้ได้ผลรวมเยอะที่สุด

เช่น ABbCcc => c*26 + b*25 + a*24 = 152

อย่าลืม ตามสไตล์พวกเรา ห้ามใช้ loop และห้าม reassign ค่าให้กับตัวแปรครับ

ผมแก้ปัญหาข้อนี้ได้ด้วย code ชุดนี้

s = 'ABbCcc'
s.split('').map(&:downcase).select { |c| c =~ /[a-z]/ }
 .group_by(&:to_s).map { |_,v| v.count }.sort { |x,y| y <=> x }
 .zip((1..26).to_a.reverse).inject(0) { |a, (n,e)| a + n*e }

เห็น code ต่อกันยาวพรึดแบบนี้ อาจจะดูน่ากลัวไปซักหน่อย แต่ถ้ามาเจาะดูแต่ละขั้นตอนแล้ว ทุกอย่างเข้าใจได้ง่ายมาก

s.split('') #=> ["A", "B", "b", "C", "c", "c"]
.map(&:downcase) #=> ["a", "b", "b", "c", "c", "c"]
.select { |c| c =~ /[a-z]/ } #=> ["a", "b", "b", "c", "c", "c"]
.group_by(&:to_s) #=> {"a"=>["a"], "b"=>["b", "b"], "c"=>["c", "c", "c"]}
.map { |_,v| v.count } #=> [1, 2, 3]
.sort { |x,y| y <=> x } #=> [3, 2, 1]
.zip((1..26).to_a.reverse) #=> [[3, 26], [2, 25], [1, 24]]
.inject(0) { |a, (n,e)| a + n*e } #=> 152

มาลองดูด้วย Clojure กันบ้าง

(->> "ABbCcc"
     (re-seq #"[a-zA-Z]")
     (map clojure.string/lower-case)
     (group-by identity)
     (map #(count (last %)))
     (sort >)
     (zipmap (range 26 1 -1))
     (map #(* (key %) (val %)))
     (reduce +))

ปล. แอบไปดูเฉลยจากทาง Facebook มา พบว่าเค้าใช้วิธีเดียวกับเราเป๊ะ แต่เขียนด้วย Python

ขี้เกียจเขียน For loop ภาค 2

หลังจากคราวที่ผมได้มาเล่าไปแล้วว่าผมเอาตัวรอดในฐานะโปรแกรมเมอร์มาได้ไงเป็นเวลากว่าปีโดยไม่ใช้ for loop ใครยังไม่ได้ติดตามความขี้เกียจครั้งที่แล้ว เชิญติดตามได้ทางลิงค์นี้ครับ ขี้เกียจเขียน For loop คราวนี้ จะมาเล่าถึงเทคนิคนึง ซึ่งผมเพิ่งได้เรียนรู้เมื่อไม่กี่เดือนมานี้เอง คิดว่าน่าจะมีประโยชน์กับท่านที่สนใจศาสตร์ทาง functional programming และ immutable data ไม่มากก็น้อย

ปัญหาก็มีอยู่ว่า เมื่อเราไม่มี for loop แล้ว ไม่มีไอ่เจ้า i++ แล้ว ถ้าเราจำเป็นต้องใช้เจ้าตัวเลขลำดับ (i) นี้จะทำยังไง

ตัวอย่างของปัญหาในครั้งนี้ คือ ฟอร์แมทผลลัพธ์ของการแข่งขันเขียนโปรแกรม ซึ่งไม่ว่าจะเป็นทั้ง Google Code Jam หรือ Facebook Hacker Cup จะผู้แข่งขันสร้างไฟล์สำหรับผมลัพธ์ซึ่งมีหน้าตาเป็นแบบนี้

Case #1: 100
Case #2: 200
Case #3: 300
...

เราจะมาเขียน code ที่รับอาเรย์ของผลลัพธ์แต่ละข้อมา(เช่น [100,200,300,…]) แล้วสร้าง string ของฟอร์แมทผลลัพท์เหมือนด้านบนกัน โดยอย่าลืมว่าเราไม่มี for loop นะ

เริ่มต้นด้วยวิธีแรกที่ชาว Ruby นิยมใช้กัน each_with_index

output = [100,200,300]
result = ''
output.each_with_index do |e,i|
  result += "Case ##{i+1}: #{e}\n"
end
puts result

วิธีนี้ใช้ได้ดีครับ ติดอยู่เพียงแค่ว่ามันมีการ assign ค่าใหม่หลายครั้งให้แก่ตัวแปร result ไม่ถูกใจขาโจ๋ pure function, immutable data อย่างเราเลย

มาดูวิธีต่อไป วิธีนี้ไม่มีการ assign ค่าให้แก่ตัวแปรเลย แต่ขออนุญาตสงวนสิทธิ์ห้ามลอกเลียนแบบนำไปใช้นะครับ ผมเคยใช้วิธีนี้ตอนสมัยหัด functional programming ใหม่ๆ ด้วย Scala เอาไปแข่ง Code Jam ครับ ผลลัพธ์ครับ ทำไม่เสร็จซักข้อครับ

วิธีนั้นคือ recursive ครับ

def results(a, i)
  if a.empty?
    ''
  else
    "Case ##{i}: #{a.first}\n" + results(a[1..-1], i+1)
  end
end
results([100,200,300], 1)

เหนื่อยมากครับ แต่ก็ถือว่าเอาตัวรอดมาได้ แบบไม่ต้องมีการ assign ตัวแปรเลยครับ

มาดูวิธีที่สามกันบ้าง ซึ่งเป็นวิธีที่แสดงเทคนิคที่ทำให้ผมมานั่งเขียน blog นี้กันครับ

 output = [100,200,300]
 index = (1..output.length).to_a
 output.zip(index).map { |(e,i)| "Case ##{i}: #{e}" }.join("\n")
 

เทคนิคมันอยู่ตรงไอ่เจ้า zip นี่แหละครับ ในเมื่อไม่มี i ก็หา i มาแปะเข้าไปซะเลย คราวนี้ก็จะได้คู่ตัวเลข e และ i ไปทำอะไรก็ได้ตามใจเราครับ จะประยุกต์ให้เป็น i+2, i*8 ยังไงตามใจเรา เลือกสร้างอาเรย์ของ “i” แบบตัวแปร index ได้ตามความเหมาะสมเลยครับ

เจ้า zip นี้ชื่ออาจจะต่างกันไปในแต่ละภาษาครับ โดยการทำงานของมันคือเอาอาเรย์มา 2 (หรือหลายอัน) แล้วสร้างอาเรย์ใหม่โดยกรุ๊ปค่าในตำแหน่งเดียวกันเข้าด้วยกันครับ