유용성 / 유지 보수성을 위해 더 나은 디자인이 더 나은 것이 무엇인지, 그리고 커뮤니티와 잘 어울리는 것이 더 좋은지 궁금합니다.
주어진 데이터 모델 :
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)
예를 들어 항목이나 상점 등을 추가하여 사용자를 변환하기 위해 모나 딕 함수를 구현할 수 있지만 유효하지 않은 사용자가 생길 수 있으므로 모나드 함수는 사용자가 만들고 생성하는 사용자를 확인해야합니다.
그래서 나는 단지 :
- 오류 모나드에 싸서 모나드 함수가 유효성 검사를 실행하도록하십시오.
- 오류 모나드에 싸서 소비자가 적절한 오류 응답을 던지는 순서로 모나드 유효성 검사 함수를 바인딩하도록합니다 (따라서 유효하지 않은 사용자 개체를 확인하지 않고 선택할 수 있습니다)
- 실제로 사용자의 바인드 인스턴스에 빌드하여 모든 바인드마다 자동으로 유효성 검사를 실행하는 자체 오류 모나드를 효과적으로 만듭니다.
세 가지 접근 방식 각각에 대한 긍정과 부정을 볼 수 있지만이 시나리오에서 커뮤니티가 더 일반적으로 수행하는 작업을 알고 싶습니다.
따라서 코드 용어에서 옵션 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"]