วิธีที่อธิบายถึงปัญหา "แบบจำลองโลหิตจาง" นั้นแปลได้ไม่ดีนักสำหรับ FP ก่อนอื่นจะต้องมีการจัดวางให้เหมาะสมโดยทั่วไป หัวใจของมันคือโมเดลโลหิตจางซึ่งเป็นแบบจำลองที่มีความรู้เกี่ยวกับวิธีการใช้อย่างถูกต้องซึ่งไม่ได้ถูกห่อหุ้มด้วยตัวมันเอง แต่ความรู้นั้นแพร่กระจายไปทั่วกองบริการที่เกี่ยวข้อง บริการเหล่านั้นควรจะลูกค้าของรูปแบบ แต่เนื่องจากโรคโลหิตจางของพวกเขากำลังจัดขึ้นรับผิดชอบสำหรับมัน ตัวอย่างเช่นพิจารณาAccount
คลาสที่ไม่สามารถใช้เพื่อเปิดใช้งานหรือปิดใช้งานบัญชีหรือแม้กระทั่งการค้นหาข้อมูลเกี่ยวกับบัญชีเว้นแต่ว่าจัดการผ่านAccountManager
คลาส บัญชีควรจะรับผิดชอบการดำเนินงานขั้นพื้นฐานของมันไม่ใช่คลาสของผู้จัดการภายนอก
ในการเขียนโปรแกรมการทำงานปัญหาที่คล้ายกันเกิดขึ้นเมื่อชนิดข้อมูลไม่ได้แสดงสิ่งที่พวกเขาควรจะทำแบบจำลองได้อย่างถูกต้อง สมมติว่าเราจำเป็นต้องกำหนดประเภทที่แสดง ID ผู้ใช้ นิยาม "โลหิตจาง" จะระบุว่า ID ผู้ใช้เป็นสตริง เป็นไปได้ทางเทคนิค แต่พบปัญหาใหญ่เพราะ ID ผู้ใช้ไม่ได้ใช้เหมือนสตริงโดยพลการ มันไม่มีเหตุผลที่จะต่อพวกมันเข้าด้วยกันหรือตัดแบ่งสตริงย่อยของพวกเขาออก Unicode ไม่สำคัญและควรฝังใน URL และบริบทอื่น ๆ ได้อย่างง่ายดายด้วยข้อ จำกัด ของอักขระและรูปแบบที่เข้มงวด
การแก้ไขปัญหานี้มักเกิดขึ้นในไม่กี่ขั้นตอน การตัดครั้งแรกอย่างง่าย ๆ คือการพูดว่า "เอ่อเอUserID
แทนด้วยสตริง แต่มันเป็นประเภทที่แตกต่างกันและคุณไม่สามารถใช้งานได้ตามที่คุณคาดหวังไว้" Haskell (และภาษาที่ใช้งานได้บางประเภท) มีคุณสมบัตินี้ผ่านnewtype
:
newtype UserID = UserID String
นี้กำหนดUserID
ฟังก์ชั่นซึ่งเมื่อกำหนดString
โครงสร้างค่าที่เป็นรับการปฏิบัติเหมือนUserID
โดยระบบการพิมพ์ แต่ที่ยังคงเป็นเพียงString
ที่รันไทม์ ตอนนี้ฟังก์ชั่นสามารถประกาศว่าพวกเขาต้องการUserID
แทนสตริง; ใช้UserID
s ที่ซึ่งก่อนหน้านี้คุณกำลังใช้สายอักขระที่ป้องกันการต่อโค้ดสองUserID
ตัวเข้าด้วยกัน ระบบประเภทรับประกันได้ว่าไม่สามารถเกิดขึ้นได้โดยไม่ต้องทำการทดสอบ
จุดอ่อนที่นี่คือรหัสที่ยังสามารถใช้ใด ๆ ที่String
ชอบ"hello"
และสร้างUserID
จากมัน ขั้นตอนเพิ่มเติมรวมถึงการสร้างฟังก์ชั่น "ตัวสร้างสมาร์ท" ซึ่งเมื่อได้รับสตริงตรวจสอบค่าคงที่และจะส่งกลับUserID
ถ้าพวกเขาพอใจ จากนั้นตัวสร้าง "ใบ้" UserID
จะถูกทำให้เป็นส่วนตัวดังนั้นหากลูกค้าต้องการให้UserID
พวกเขาต้องใช้ตัวสร้างอัจฉริยะซึ่งจะเป็นการป้องกันไม่ให้ UserID ที่มีรูปแบบไม่ถูกต้อง
ขั้นตอนต่อไปยังกำหนดUserID
ประเภทข้อมูลในลักษณะที่เป็นไปไม่ได้ที่จะสร้างรูปแบบที่ไม่ถูกต้องหรือ "ไม่เหมาะสม" เพียงแค่นิยาม ตัวอย่างเช่นการกำหนดUserID
เป็นรายการของตัวเลข:
data Digit = Zero | One | Two | Three | Four | Five | Six | Seven | Eight | Nine
data UserID = UserID [Digit]
เพื่อสร้างUserID
รายการของตัวเลขจะต้องให้ ตามคำนิยามนี้มันเป็นเรื่องเล็กน้อยที่จะแสดงให้เห็นว่าเป็นไปไม่ได้ที่UserID
จะมีอยู่ซึ่งไม่สามารถแสดงใน URL การกำหนดรูปแบบข้อมูลเช่นนี้ใน Haskell มักได้รับความช่วยเหลือจากคุณสมบัติระบบขั้นสูงเช่นชนิดข้อมูลและประเภทข้อมูลเชิงพีชคณิตทั่วไป (GADT)ซึ่งช่วยให้ระบบประเภทสามารถกำหนดและพิสูจน์ค่าคงที่เพิ่มเติมเกี่ยวกับรหัสของคุณ เมื่อข้อมูลถูกแยกออกจากพฤติกรรมคำจำกัดความข้อมูลของคุณเป็นเพียงวิธีการเดียวที่คุณต้องบังคับใช้พฤติกรรม