ดีกว่าที่จะใช้ข้อผิดพลาด monad กับการตรวจสอบในฟังก์ชั่น monadic ของคุณหรือใช้ monad ของคุณเองด้วยการตรวจสอบโดยตรงในการผูกของคุณ?


9

ฉันสงสัยว่าการออกแบบที่ดีกว่าคืออะไรสำหรับการใช้งาน / การบำรุงรักษาและสิ่งที่ดีกว่าเท่าที่เหมาะสมกับชุมชน

รับรูปแบบข้อมูล:

type Name = String

data Amount = Out | Some | Enough | Plenty deriving (Show, Eq)
data Container = Container Name deriving (Show, Eq)
data Category = Category Name deriving (Show, Eq)
data Store = Store Name [Category] deriving (Show, Eq)
data Item = Item Name Container Category Amount Store deriving Show
instance Eq (Item) where
  (==) i1 i2 = (getItemName i1) == (getItemName i2)

data User = User Name [Container] [Category] [Store] [Item] deriving Show
instance Eq (User) where
  (==) u1 u2 = (getName u1) == (getName u2)

ฉันสามารถใช้ฟังก์ชั่น monadic เพื่อแปลงผู้ใช้ตัวอย่างโดยการเพิ่มรายการหรือร้านค้า ฯลฯ แต่ฉันอาจท้ายด้วยผู้ใช้ที่ไม่ถูกต้องดังนั้นฟังก์ชัน monadic เหล่านั้นจะต้องตรวจสอบความถูกต้องของผู้ใช้ที่พวกเขาได้รับและสร้าง

ดังนั้นฉันควร:

  • ห่อไว้ในข้อผิดพลาด monad และทำให้ฟังก์ชัน monadic ดำเนินการตรวจสอบความถูกต้อง
  • ห่อไว้ในข้อผิดพลาด monad และทำให้ผู้บริโภคผูกฟังก์ชันการตรวจสอบความถูกต้องตามลำดับที่ส่งการตอบสนองข้อผิดพลาดที่เหมาะสม (เพื่อให้พวกเขาสามารถเลือกที่จะไม่ตรวจสอบและพกพาวัตถุผู้ใช้ที่ไม่ถูกต้อง)
  • จริง ๆ แล้วสร้างเป็นอินสแตนซ์ของการผูกบนผู้ใช้อย่างมีประสิทธิภาพในการสร้างข้อผิดพลาดของฉันเองชนิด monad ที่ดำเนินการตรวจสอบกับทุกผูกโดยอัตโนมัติ

ฉันสามารถเห็นข้อดีและข้อเสียของวิธีการทั้ง 3 วิธีนี้ แต่ต้องการทราบว่าชุมชนทำอะไรกันมากขึ้นสำหรับสถานการณ์นี้

ดังนั้นในแง่ของโค้ดอย่างตัวเลือก 1:

addStore s (User n1 c1 c2 s1 i1) = validate $ User n1 c1 c2 (s:s1) i1
updateUsersTable $ someUser >>= addStore $ Store "yay" ["category that doesnt exist, invalid argh"]

ตัวเลือก 2:

addStore s (User n1 c1 c2 s1 i1) = Right $ User n1 c1 c2 (s:s1) i1
updateUsersTable $ Right someUser >>= addStore $ Store "yay" ["category that doesnt exist, invalid argh"] >>= validate
-- in this choice, the validation could be pushed off to last possible moment (like inside updateUsersTable before db gets updated)

ตัวเลือก 3:

data ValidUser u = ValidUser u | InvalidUser u
instance Monad ValidUser where
    (>>=) (ValidUser u) f = case return u of (ValidUser x) -> return f x; (InvalidUser y) -> return y
    (>>=) (InvalidUser u) f = InvalidUser u
    return u = validate u

addStore (Store s, User u, ValidUser vu) => s -> u -> vu
addStore s (User n1 c1 c2 s1 i1) = return $ User n1 c1 c2 (s:s1) i1
updateUsersTable $ someValidUser >>= addStore $ Store "yay" ["category that doesnt exist, invalid argh"]

คำตอบ:


5

ฉันต้องการถามตัวเองว่ามีUserข้อผิดพลาดของรหัสหรือสถานการณ์ที่อาจเกิดขึ้นตามปกติ (ตัวอย่างเช่นมีคนป้อนข้อมูลที่ไม่ถูกต้องในแอปพลิเคชันของคุณ) หากเป็นข้อบกพร่องฉันจะพยายามให้แน่ใจว่ามันจะไม่เกิดขึ้น (เช่นการใช้ตัวสร้างอัจฉริยะหรือการสร้างประเภทที่ซับซ้อนยิ่งขึ้น)

หากเป็นสถานการณ์ที่ถูกต้องการประมวลผลข้อผิดพลาดบางอย่างในระหว่างรันไทม์นั้นเหมาะสม แล้วผมถามว่ามันจะมีอะไรจริงๆหมายสำหรับผมว่าUserเป็นที่ไม่ถูกต้อง ?

  1. หมายความว่าไม่ถูกต้องUserสามารถทำให้รหัสบางอย่างล้มเหลว อย่าส่วนของรหัสของคุณพึ่งพาความจริงที่ว่าUserอยู่เสมอถูกต้อง?
  2. หรือหมายความว่ามันไม่สอดคล้องกันซึ่งจำเป็นต้องได้รับการแก้ไขในภายหลัง แต่ไม่ทำลายอะไรระหว่างการคำนวณ?

หากเป็น 1. ฉันจะไปหา monad error บางประเภท (ไม่ว่าจะเป็นมาตรฐานหรือแบบของคุณเอง) มิฉะนั้นคุณจะเสียการรับประกันว่ารหัสของคุณทำงานได้อย่างถูกต้อง

การสร้าง monad ของคุณเองหรือใช้ stack ของ monad transformers เป็นอีกปัญหาหนึ่งนี่อาจจะมีประโยชน์: มีใครเคยเจอ Monad Transformer บ้างไหม? .


อัปเดต:ดูตัวเลือกเพิ่มเติมของคุณ:

  1. ดูเหมือนจะเป็นวิธีที่ดีที่สุด บางทีเพื่อความปลอดภัยจริงๆฉันต้องการซ่อนตัวสร้างUserและแทนที่จะส่งออกฟังก์ชั่นบางอย่างที่ไม่อนุญาตให้สร้างอินสแตนซ์ที่ไม่ถูกต้องแทน ด้วยวิธีนี้คุณจะมั่นใจได้ว่าทุกครั้งที่มันเกิดขึ้นจะได้รับการจัดการอย่างเหมาะสม ตัวอย่างเช่นฟังก์ชั่นทั่วไปสำหรับการสร้างUserอาจเป็นสิ่งที่ต้องการ

    user :: ... -> Either YourErrorType User
    -- more generic:
    user :: (MonadError YourErrorType m) ... -> m User
    -- Or if you actually don't need to differentiate errors:
    user :: ... -> Maybe User
    -- or more generic:
    user :: (MonadPlus m) ... -> m User
    -- etc.
    

    ห้องสมุดหลายคนใช้เวลา appropach ที่คล้ายกันเช่นMap, SetหรือSeqซ่อนการดำเนินงานพื้นฐานเพื่อที่ว่ามันเป็นไปไม่ได้ที่จะสร้างโครงสร้างที่ไม่เชื่อฟังค่าคงที่ของพวกเขา

  2. หากคุณเลื่อนการตรวจสอบไปยังจุดสิ้นสุดและใช้Right ...ทุกที่คุณไม่จำเป็นต้องใช้ Monad อีกต่อไป คุณสามารถทำการคำนวณที่บริสุทธิ์และแก้ไขข้อผิดพลาดที่อาจเกิดขึ้นได้ในตอนท้าย IMHO วิธีการนี้มีความเสี่ยงมากเนื่องจากค่าผู้ใช้ที่ไม่ถูกต้องสามารถนำไปสู่การมีข้อมูลที่ไม่ถูกต้องได้เนื่องจากคุณไม่ได้หยุดการคำนวณในไม่ช้า และถ้าเกิดว่าวิธีการอื่นปรับปรุงผู้ใช้เพื่อให้สามารถใช้ได้อีกครั้งคุณจะพบว่ามีข้อมูลที่ไม่ถูกต้องอยู่ที่ไหนสักแห่งและไม่รู้ด้วยซ้ำ

  3. มีปัญหาหลายอย่างที่นี่

    • ที่สำคัญที่สุดคือว่า monad ต้องยอมรับพารามิเตอร์ชนิดใด ๆ Userที่ไม่เพียง ดังนั้นคุณvalidateจะต้องมีประเภทu -> ValidUser uโดยไม่มีข้อ จำกัด uใด ดังนั้นจึงเป็นไปไม่ได้ที่จะเขียน monad ที่ตรวจสอบความถูกต้องของอินพุตreturnเพราะreturnต้องเป็น polymorhpic ทั้งหมด
    • ถัดไปสิ่งที่ฉันไม่เข้าใจว่าคุณตรงกับในในความหมายของcase return u of >>=ประเด็นหลักคือValidUserควรแยกความแตกต่างของค่าที่ถูกต้องและไม่ถูกต้องดังนั้น monad จึงต้องแน่ใจว่าสิ่งนี้เป็นจริงเสมอ ดังนั้นมันอาจเป็นเพียงแค่

      (>>=) (ValidUser u) f = f u
      (>>=) (InvalidUser u) f = InvalidUser u
      

    Eitherและนี้แล้วดูมากเช่น

โดยทั่วไปฉันจะใช้ monad แบบกำหนดเองก็ต่อเมื่อ

  • ไม่มีพระอยู่แล้วที่ให้คุณใช้งานได้ตามที่คุณต้องการ Monads ที่มีอยู่มักจะมีฟังก์ชั่นสนับสนุนมากมายและที่สำคัญพวกมันมี monad transformers เพื่อให้คุณสามารถเขียนมันเป็น monad stack
  • หรือถ้าคุณต้องการ monad ที่ซับซ้อนเกินกว่าที่จะอธิบายว่าเป็น monad stack

จุดสองจุดสุดท้ายของคุณมีค่าและฉันไม่ได้คิดถึงพวกเขา! ภูมิปัญญาที่ฉันกำลังค้นหาแน่นอนขอบคุณที่แบ่งปันความคิดของคุณฉันจะไปกับ # 1!
จิมมี่ฮอฟฟา

เพิ่งผูกโมดุลทั้งหมดเมื่อคืนนี้และคุณก็ตายไปแล้ว ฉันทำให้วิธีการตรวจสอบของฉันกลายเป็น combinators สำคัญ ๆ ที่ฉันได้ทำการอัปเดตโมเดลทั้งหมดและมันก็สมเหตุสมผลดีกว่านี้ ฉันกำลังจะไปหลังจาก # 3 และตอนนี้ฉันเห็นว่า ... ยืดหยุ่นวิธีการที่จะได้รับดังนั้นขอบคุณตันสำหรับการตั้งค่าฉันตรง!
จิมมี่ฮอฟฟา
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.