【发布时间】:2014-03-28 12:06:06
【问题描述】:
我正在尝试使用 monad 阅读器在 Scala 中进行依赖注入。我最近开始学习 Scala,所以我在这里给出的代码无法编译,但我希望我的问题变得清晰。首先,假设我们的应用程序允许用户更改密码。首先,我创建一个简单的案例类 User 并在伴随对象上添加一个 changePassword 方法:
case class User (id:Int, username:String, password:String)
object User {
def changePassword (oldPassword:String, newPassword:String, user:User) = {
if (!user.password.equals(oldPassword)) {
-\/("Old password incorrect")
} else {
\/-(user.copy(password = newPassword))
}
}
}
请注意,changePassword 方法的返回类型仍然有些具体。在 Haskell 中我会写:
data User = User {
id :: Int
, username :: String
, password :: String
} deriving (Show)
changePassword :: (MonadError String m) => String -> String -> User -> m User
changePassword old new user =
if password user == old
then return $ user { password = new }
else throwError "Old password incorrect"
这将允许在任何包含 Error monad 的 monad 转换器堆栈中使用 changePassword 函数。
现在,要创建应用程序,我们还需要另外两个组件。一个组件是知道如何检索和存储用户对象的存储库。可能存在多种实现。例如,我们可能有一个生产环境中的数据库存储库和一个用于测试目的的内存存储库。
trait UserRepository {
def getById(id:Int):M[User]
def save (user:User):M[Unit]
}
object DatabaseUserRepository extends UserRepository {
def getById(id:Int):MonadReader[Connection,User]
def save (user:User):MonadReader[Connection,Unit]
}
object InMemoryUserRepository extends UserRepository {
def getById(id:Int):MonadState[UserMap,User]
def save (user:User):MonadState[UserMap,Unit]
}
两种实现都是一元的,但它们需要的一元行为可能不同。 IE。数据库存储库依赖于可以使用 reader monad 访问的连接,而内存存储库依赖于 state monad。
另一个组件是一个服务组件,它充当从 UI 到我们逻辑的入口点。
object UserService {
def doChangePassword (id:Int, oldPassword:String, newPassword:String):MonadReader[UserRepository, Unit]
}
此组件使用用户存储库通过给定的 id 检索用户,然后调用 changePassword 函数并使用存储库保存更新的用户对象。
我希望这能说明我试图实现的目标。但是,我仍然有点困惑如何将不同的部分连接在一起......
【问题讨论】:
标签: scala dependency-injection monads monad-transformers