Me pregunto qué es mejor en cuanto al diseño para la usabilidad / mantenibilidad, y qué es mejor en lo que respecta a la comunidad.
Dado el modelo de datos:
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)
Puedo implementar funciones monádicas para transformar el usuario, por ejemplo, agregando elementos o tiendas, etc., pero puedo terminar con un usuario no válido, por lo que esas funciones monádicas tendrían que validar el usuario que obtienen o crean.
Entonces, ¿debería simplemente:
- envolverlo en una mónada de error y hacer que las funciones monádicas ejecuten la validación
- envuélvala en una mónada de error y haga que el consumidor vincule una función de validación monádica en la secuencia que arroja la respuesta de error apropiada (para que pueda elegir no validar y transportar un objeto de usuario no válido)
- en realidad lo construyo en una instancia de enlace en el usuario creando efectivamente mi propio tipo de mónada de error que ejecuta la validación con cada enlace automáticamente
Puedo ver aspectos positivos y negativos de cada uno de los 3 enfoques, pero quiero saber qué hace más comúnmente la comunidad para este escenario.
Entonces, en términos de código, como la opción 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"]
opcion 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)
opción 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"]
fuente