Scala 中的配置数据——我应该使用 Reader monad 吗?

2024-01-12

如何在 Scala 中创建功能正常的可配置对象?我在网上看过托尼·莫里斯的视频Readermonad 和我仍然无法将这些点联系起来。

我有一个硬编码的列表Client对象:

class Client(name : String, age : Int){ /* etc */}

object Client{
  //Horrible!
  val clients  = List(Client("Bob", 20), Client("Cindy", 30))
}

I want Client.clients在运行时确定,可以灵活地从属性文件或数据库中读取。在 Java 世界中,我定义一个接口,实现两种类型的源,并使用 DI 分配一个类变量:

trait ConfigSource { 
  def clients : List[Client]
}

object ConfigFileSource extends ConfigSource {
  override def clients = buildClientsFromProperties(Properties("clients.properties"))  
  //...etc, read properties files 
}

object DatabaseSource extends ConfigSource { /* etc */ }

object Client {
  @Resource("configuration_source") 
  private var config : ConfigSource = _ //Inject it at runtime  

  val clients = config.clients 
} 

对我来说,这似乎是一个非常干净的解决方案(代码不多,意图明确),但是var does跳出来(OTOH,在我看来这不是really麻烦了,既然我知道了will只能注射一次)。

会发生什么Readermonad 在这种情况下看起来像,像我 5 岁一样向我解释一下,它的优点是什么?


让我们从你的方法和其他方法之间简单、表面的区别开始Reader方法,即您不再需要坚持config任何地方。假设您定义了以下模糊巧妙的类型同义词:

type Configured[A] = ConfigSource => A

现在,如果我需要一个ConfigSource对于某些函数,假设一个函数得到n列表中的第一个客户端,我可以将该函数声明为“已配置”:

def nthClient(n: Int): Configured[Client] = {
  config => config.clients(n)
}

所以我们本质上是在拉一个config凭空而来,任何时候我们需要的时候!听起来像依赖注入,对吧?现在假设我们想要列表中第一、第二和第三个客户的年龄(假设它们存在):

def ages: Configured[(Int, Int, Int)] =
  for {
    a0 <- nthClient(0)
    a1 <- nthClient(1)
    a2 <- nthClient(2)
  } yield (a0.age, a1.age, a2.age)

为此,当然,您需要一些适当的定义map and flatMap。我不会在这里讨论这个问题,只是简单地说 Scalaz(或Rúnar 精彩的 NEScala 演讲 http://www.youtube.com/watch?v=ZasXwtTRkio, or Tony's http://vimeo.com/20674558您已经看到过)为您提供了您需要的一切。

这里重要的一点是ConfigSource依赖及其所谓的注入大多是隐藏的。我们在这里可以看到的唯一“提示”是ages属于类型Configured[(Int, Int, Int)]而不是简单地(Int, Int, Int)。我们不需要明确引用config任何地方。

作为旁白,这就是我几乎总是喜欢思考单子的方式:它们隐藏它们的效果所以它不会污染你的代码流,同时明确声明效果在类型签名中。换句话说,你不必重复太多:你说“嘿,这个函数处理effect X" 在函数的返回类型中,不要再搞乱它了。

在这个例子中,效果当然是从某个固定的环境中读取。您可能熟悉的另一个一元效应包括错误处理:我们可以说Option隐藏错误处理逻辑,同时在方法的类型中明确出现错误的可能性。或者,与阅读相反,Writermonad 隐藏了我们正在写入的内容,同时使其在类型系统中显式存在。

最后,就像我们通常需要引导 DI 框架一样(在我们通常的控制流之外的某个地方,例如在 XML 文件中),我们也需要引导这个奇怪的 monad。当然,我们的代码会有一些逻辑入口点,例如:

def run: Configured[Unit] = // ...

它最终变得非常简单:因为Configured[A]只是函数的类型同义词ConfigSource => A,我们可以将函数应用到它的“环境”:

run(ConfigFileSource)
// or
run(DatabaseSource)

哒哒!因此,与传统的 Java 风格 DI 方法相比,我们这里没有任何“魔法”。可以说,唯一的魔力被封装在我们的定义中Configured类型及其作为 monad 的行为方式。最重要的是,类型系统让我们保持诚实关于哪个“领域”依赖注入发生在:任何具有类型的东西Configured[...]存在于 DI 世界中,没有它的任何事物都不是。我们在老式 DI 中根本无法理解这一点,其中一切可能是由魔法管理的,因此您并不真正知道代码的哪些部分可以在 DI 框架之外安全地重用(例如,在单元测试中或完全在其他项目中)。


update:我写了一个博客文章 http://mergeconflict.com/reading-your-future这解释了Reader更详细地说。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Scala 中的配置数据——我应该使用 Reader monad 吗? 的相关文章

随机推荐

  • 使用Powershell安装系统字体

    我有一个文件夹 里面装满了自定义字体的 TTF 文件 我需要使用 powershell 脚本将它们安装为系统字体 这是在 Windows Server 2008 R2 上 有人知道如何在 powershell 中做到这一点吗 谢谢 这很简单
  • 从 HttpClient.GetStringAsync 读取响应

    我正在使用 Windows Phone Store 应用程序的新运行时开发 Windows 通用应用程序 我使用以下代码向服务器发送请求 并期望返回 HTML 响应 但是 当我返回字符串并将其显示在 UI 中时 它只是说 System Th
  • 印地语到英语音译[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 有没有一个可以将印地语音译成英语的Python库 例如 应转换为 khaya 我所知道的不太蹩脚的转码器是散文 https githu
  • 填充数组时检查重复项

    我有一个数组 其中填充了 6 个随机生成的数字 首先 它生成一个 1 到 49 之间的随机数 然后将其与数组中的数字进行检查 如果发现重复 则应再次生成随机数 然后再次执行检查 如果没有重复项 则将该数字添加到数组中 这是代码 public
  • git hunk 编辑模式 - 如何删除“-”行?

    bbb aaa To remove lines make them lines context To remove lines delete them Lines starting with will be removed If the p
  • 正则表达式负向前瞻

    我需要修改这个正则表达式 href 与此匹配 href pothole locator map aspx lang en gb lat 53 153977 lng 3 533306 为了不匹配这个 href pothole locator
  • hibernate 5 + ZonedDateTime + postgresql 包括时区和偏移量

    我有一个正在运行的应用程序 spring boot 1 3 hibernate 5 java 8 ZonedDateTime postgresql 在其中一个表中我有以下字段 Column name DATE ENABLED Type ty
  • 您能想到 .NET 中具有同步上下文的事件模式吗?

    主要问题是 从一个线程引发事件可以调用只能在特定线程上下文中调用的委托 做了一些研究后这个问题 https stackoverflow com questions 10466022 how to raise a static event t
  • Pandas 映射到一个新列,SettingWithCopyWarning [重复]

    这个问题在这里已经有答案了 在 pandas 数据框中 我尝试映射 df old column 应用用户定义的函数f为每一行创建一个新列 df new column df old column map lambda x f x 这将给出 S
  • Getter、Setter 和属性最佳实践。 Java 与 C#

    我现在正在上 C 课程 我正在努力找出最好的做事方式 我有 Java 背景 所以我只熟悉 Java 最佳实践 我是 C 新手 在 Java 中 如果我有私有财产 我会这样做 private String name public void s
  • file_get_contents 抛出 400 Bad Request 错误 PHP

    我只是用一个file get contents 获取来自用户的最新推文 如下所示 tweet json decode file get contents http api twitter com 1 statuses user timeli
  • python imshow 灰度静态颜色值

    我知道使用 matplotlib pyplot 的 imshow 给了我一个很好的草图 可以用来可视化矩阵 我的问题是 当我想要可视化矩阵时 该函数会根据我传递的值调整颜色密度 例如 define a numpy matrix with v
  • 如何在 Go 中使用双星 glob?

    这好像是Go是少数几种似乎不理解文件通配符双星 globstar 语法的语言之一 至少这似乎没有按预期工作 filepath Glob dir bundle txt 我是否遗漏了一些关于filepath执行 周围有支持这个的库吗 The f
  • 使用 git 应用 diff 文件

    所以我试图将 diff 文件应用到我的 git dev 分支 我想应用的差异是这里的 https github com mbabker joomla cms compare JHtml move patch https github com
  • 了解 webrtc 转储

    我从下载了转储chrome webrtc internals 现在我想从这些数据中理解并绘制一些图表以显示数据包丢失和转储中包含的其他内容 我可以看到其中的图表chrome webrtc internals只是 但我想自己做这些事情 并在过
  • 当匹配有效时,简单术语查询不能与弹性一起使用

    我在 Elastic 中有一个如下所示的 JSON 对象 source version 1 object id f1dcae27 7a6f 4fea b540 901c09b60a15 object name testFileName fo
  • 从 Git 存储库中删除所有标签

    我想从 Git 存储库中删除所有标签 我怎样才能做到这一点 Using git tag d tagname删除标签tagname本地 并使用git push tags我更新了 git 提供者上的标签 I tried git tag d 但我
  • 使用 PowerShell 2.0 将多个 XML 文件合并为一个?

    我有一个非常大的 XML 文件的目录 其结构如下 文件1 xml
  • 读取远程 mp3 文件的 ID3 标签?

    使用 Silverlight 读取 MP3 标签 https stackoverflow com questions 1477835 read mp3 tags with silverlight让我开始阅读 id3 标签 但我意识到 tag
  • Scala 中的配置数据——我应该使用 Reader monad 吗?

    如何在 Scala 中创建功能正常的可配置对象 我在网上看过托尼 莫里斯的视频Readermonad 和我仍然无法将这些点联系起来 我有一个硬编码的列表Client对象 class Client name String age Int et