我想知道对于可用性/可维护性而言,更好的设计明智之举,以及与社区相适应的更好之道。
给定数据模型:
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中,并让使用者按引发适当错误响应的顺序绑定一个monadic验证函数(以便他们可以选择不验证并携带无效的用户对象)
- 实际上将其构建到User上的绑定实例中,从而有效地创建了我自己的错误monad类型,它会自动对每个绑定执行验证
我可以看到对这三种方法的正面和负面的看法,但我想知道社区对此情况最常做的事情。
所以在代码方面,选项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"]