Nattawut Phetmak
Jack of all Trades
ระบบชนิดตัวแปรของ Haskell จะแตกต่างจากภาษาอื่นๆ ตรงที่แบ่งออกได้เป็น 2 ส่วน คือ
อาจมองได้ง่ายๆ อีกอย่างว่า type คือวิธี implement ข้อมูล ส่วน type class จะเป็นตัวกำหนดความสามารถของ type นั้นๆ ครับ
ซึ่งตัวเลขใน Haskell สามารถจัดแบ่งคร่าวๆ ได้ดังนี้
Num
Integral
Int
จำนวนเต็มตามขนาด word ของคอมพิวเตอร์Integer
จำนวนเต็มในอุดมคติ ไม่มีขอบเขตบน/ล่างFloating
, Fractional
และเราสามารถตรวจสอบ type ได้โดยใช้ command :t
(หรือเต็มๆ คือ :type
) แล้วตามด้วยตัวแปร เช่น
ghci> :t 42
42 :: Num a => a
ghci> :t 1.0
1.0 :: Fractional a => a
ghci> :t 42 + 1.0
42 + 1.0 :: Fractional a => a
type class พร้อมตัวแปรที่ใช้แสดงแทนนั้น จะถูกบอกไว้ก่อนเครื่องหมาย =>
ส่วนอีกด้านจะบอก type ของตัวแปร ซึ่งในกรณีที่เราไม่ได้เจาะจง type ลงไปเช่นนี้ Haskell จะจัดมันไว้ใน type class ที่ใหญ่ที่สุดไว้ก่อน เพื่อที่ว่าถ้าดำเนินการกับ type class ที่เป็นลูกหลานของมันจะยังให้ผลลัพท์ที่ถูกต้อง
ส่วนการประกาศ type แบบเจาะจงก็ทำได้โดยใส่ 2 colon แล้วตามด้วย type ไว้ข้างหลังตัวแปรนั้น
ghci> let answer = 42 :: Float
ghci> :t answer
answer :: Float
จะเห็นว่าคราวนี้ไม่มีการบอก type class กับ =>
แล้ว เพราะเรารู้ type ที่แน่นอนของมันนั่นเอง
อย่างที่บอกไว้แต่ต้น การดำเนินการข้าม type หรือ type class ที่ไม่เกี่ยวข้องกันนั้น ไม่สามารถทำได้ใน Haskell เช่น error ในตัวอย่างนี้
ghci> (1 :: Int) + (1 :: Integer)
<interactive>:2:15:
Couldn't match expected type `Int' with actual type `Integer'
In the second argument of `(+)', namely `(1 :: Integer)'
In the expression: (1 :: Int) + (1 :: Integer)
In an equation for `it': it = (1 :: Int) + (1 :: Integer)
เลขทั้งสองตัวนั้นก็ต่างอยู่ใน type class Integral
เหมือนกัน แต่เมื่อถูกกำหนดโดย type ต่างกัน จะทำให้ไม่สามารถนำมาบวกกันได้
ทางออกคือ
fromIntegral
เพื่อเปลี่ยน type class กลับไปเป็น Num
ก่อนเอาไปคำนวณต่อtruncate
(ปัดทิ้ง), floor
(ปัดลง), ceiling
(ปัดขึ้น), round
(ปัดครึ่ง) เพื่อเปลี่ยน type class เป็น Integral
ได้