Nattawut Phetmak
Jack of all Trades
ถ้าลองใช้ :t
ตรวจชนิดของฟังก์ชันดูบ้าง จะเห็นแบบนี้
ghci> :t fromIntegral
fromIntegral :: (Integral a, Num b) => a -> b
ส่วนที่บอก type จะอยู่หลังเครื่องหมาย =>
เช่นเคย โดยฝั่งซ้ายของ ->
จะเป็นตัวแปรที่รับเข้ามาในฟังก์ชัน ส่วนฝั่งขวาคือผลลัพท์จากฟังก์ชัน นั่นหมายความว่าฟังก์ชัน fromIntegral
รับตัวแปรใน type class Integral
แล้วได้ผลลัพท์เป็น type class Num
การเขียนฟังก์ชันนั้น เช่นเดียวกับการประกาศค่าคงที่ คือใช้ let
พร้อมชื่อฟังก์ชันและตัวแปร เช่น
ghci> let half x = x / 2
ทีนี้ลองดูฟังก์ชันของ 2 ตัวแปรกันบ้าง
ghci> :t mod
mod :: Integral a => a -> a -> a
สิ่งที่แตกต่างไปจากภาษา imperative และถือได้ว่าเป็นจุดเด่นของภาษาแบบ functional เลยคือ แทนที่ type ของฟังก์ชัน 2 ตัวแปรจะใช้ช่องว่างคั่นตัวแปร (type น่าจะอยู่ในรูป a a -> a
) แต่ระหว่างตัวแปรกลับใช้สัญลักษณ์ ->
เช่นเดียวกับการบอกผลลัพท์ที่ได้จากฟังก์ชัน
เราอาจลองแบ่งกลุ่มเพื่อความกระจ่างใน type ว่ามันคือ a -> (a -> a)
ซึ่งหมายความว่า ฟังก์ชันนี้เป็นฟังก์ชันที่รับตัวแปร 1 ตัว แล้วให้ผลลัพท์เป็นฟังก์ชันที่รับตัวแปร 1 ตัวแล้วคืนค่าออกมาก็ย่อมได้
นั่นหมายความว่า ฟังก์ชัน 2 ตัวแปร แท้จริงแล้วคือฟังก์ชัน 1 ตัวแปร 2 ชั้น เราจึงสามารถเรียกฟังก์ชันแบบนี้
ghci> (mod 123456789) 1009
594
ได้เช่นกัน นี่เป็นเหตุผลว่าทำไม Haskell ถึงไม่ใช้วงเล็บล้อมรอบตัวแปร เราเรียกเทคนิคการทำให้ฟังก์ชันหลายตัวแปรกลายร่างเป็นฟังก์ชันตัวแปรเดียวซ้อนๆ กันเช่นนี้ว่า currying และเรียกฟังก์ชันที่รับตัวแปรเข้าไปแล้วบ้างแต่ยังไม่ครบว่า partial application ครับ
อนึ่ง เราสามารถเขียนการหารเก็บเศษข้างบนเป็นแบบนี้ 123456789 `mod` 1009
และถ้าเราเอาวงเล็บล้อมรอบ `mod` 1009
ด้านหลัง มันจะกลายเป็นฟังก์ชันที่รับตัวแปรไปแล้ว 1 ตัว และต้องการอีก 1 ที่ต้องทำต่อคือย้ายมันไปไว้ข้างหน้าเท่านั้นเอง
ghci> (`mod` 1009) 123456789
594
ซึ่งสามารถจับมาประกาศเป็นฟังก์ชันได้ว่า
ghci> let modWithPrime x = (`mod` 1009) x
หรือยิ่งไปกว่านั้น
ghci> let modWithPrime = (`mod` 1009)
ghci> modWithPrime 123456789
594
กลับไปดูฟังก์ชัน half
ที่เขียนไว้ตอนต้นอีกที เราลองเขียนมันใหม่เป็น
ghci> let half = (/2)
ghci> half 50
25.0
จะเห็นว่าไม่เพียงแต่ฟังก์ชันเท่านั้น ที่เราสามารถนำวงเล็บไปล้อมรอบได้ แต่นี่ยังรวมไปถึง operator ด้วย นอกจากนี้
ghci> :t (/)
(/) :: Fractional a => a -> a -> a
นั่นหมายความว่า operator เหล่านั้นก็ถือเป็นฟังก์ชันเช่นเดียวกันนั่นเอง