Nov 30, 2012

เปลี่ยนค่าตัวแปรใน Haskell

ใน ghci ถ้าเราลองทำอย่างนี้
let x = 10
let x = x + 1
กด Enter แล้วจะพบ prompt รอทำงานต่อ ถึงตอนนี้อาจคิดว่าไม่มีปัญหาหนิ แต่อย่าลืมว่า Haskell เป็น lazy language ดังนั้นต้องเรียก x ออกมาดูค่าด้วย

... แล้วเราก็จะพบกับความว่างเปล่า เฮ้ยทำไมแค่บวกเลขง่ายๆ มันถึงคิดช้าจังฟระ

นั่นเป็นเพราะว่ารูปด้านบนนี้เป็น recursive ตอนที่จะ eval ค่า x + 1 ด้านขวามือออกมา มันจะไปเรียกดู definition ของ x ซึ่งก็คือ x + 1 นั่นเอง (ลืมไปได้เลยว่าเราเคยบอกให้ x = 10)

อย่างไรก็ตาม ถ้าเราเปลี่ยนไปเขียนแบบนี้
let x = 10
let y = x + 1
let x = y
เวลาเรียก x เราจะได้คำตอบที่ถูกต้องคือ 11 แล้วครับ



แต่ถ้าทำท่าข้างบนนี้ มันก็คงบาปพอๆ กับ
word = "abracadabra"
for i in range(len(word)):
    print(word[i])
ใน Python นั่นแหละ :P



ดังนั้น Haskell จะเลี่ยงไปใช้ Monad แทน
x <- return 10
x <- return (x + 1)
ดูเผินๆ เหมือนว่าเราจะเปลี่ยนค่าตัวแปร x ได้ แต่ที่จริงแล้วสองบรรทัดข้างบนนี้จะเทียบเท่ากับ
return 10 >>= \x -> return (x + 1) >>= \x -> return x
นั่นหมายความว่า แรกสุดเราเอาค่า 10 ส่งให้ฟังก์ชั่นที่รับตัวแปร 1 ตัว โดยฟังก์ชั่นนี้อ่านชื่อตัวแปรที่รับมาว่า x แล้วจึงคำนวณ x + 1 เรียบร้อยแล้วก็ส่งต่อค่าที่คำนวณได้ไปให้ฟังก์ชั่นอีกตัว ที่รับตัวแปร 1 ตัวและอ่านชื่อตัวแปรนั้นว่า x เช่นกัน

จะเห็นว่าแท้จริงแล้วเราไม่ได้เปลี่ยนค่าของตัวแปรเลย มันเป็นเพียงการส่งต่อค่าที่คำนวณแล้วให้ฟังก์ชันอื่นไปเรื่อยๆ โดยที่ฟังก์ชั่นเหล่านั้นบังเอิญเรียกชื่อตัวแปรเหมือนกันเท่านั้นเอง