Tagless final是一种在Haskell中实现抽象类型类的方式。然而,在编写大型程序时,它可能会导致代码重复和不必要的样板代码。下面介绍一个解决这个问题的方法:
1.将共享代码提取为行为类型类
将共享代码提取为一个类型类,该类型类定义一组通用的行为函数。例如:
class Monad m => DbRepository m where
save :: Entity -> m ()
delete :: Entity -> m ()
getById :: Id -> m (Maybe Entity)
getAll :: m [Entity]
2.为每个类型实现行为类型类
为每个数据类型实现行为类型类,例如:
data RedisDb = RedisDb
instance DbRepository RedisDb where
save entity = -- save to Redis
delete entity = -- delete from Redis
getById id = -- get from Redis
getAll = -- get all from Redis
data PostgresDb = PostgresDb
instance DbRepository PostgresDb where
save entity = -- save to Postgres
delete entity = -- delete from Postgres
getById id = -- get from Postgres
getAll = -- get all from Postgres
3.将类型实例化并注入依赖项
现在,您可以在其他模块中使用这些类型,并将它们作为参数传递给函数。例如:
getUser :: (DbRepository m) => Id -> m (Maybe User)
getUser id = do
maybeEntity <- getById id
return $ case maybeEntity of
Just entity -> Just $ entityToUser entity
otherwise -> Nothing
getUserFromRedis :: RedisDb -> Id -> Maybe User
getUserFromRedis redis id = runReader (getUser id) redis
getUserFromPostgres :: PostgresDb -> Id -> Maybe User
getUserFromPostgres postgres id = runReader (getUser id) postgres
这种方法有助于避免tagless final中的样板代码,并允许您在程序中使用多种实现。