【问题标题】:What is this @@ operator defined in scalaz?scalaz 中定义的 @@ 运算符是什么?
【发布时间】:2017-02-22 15:58:22
【问题描述】:

浏览piece of Scala code at aws-scala by Atlassian 时,您可以找到以下行:

type QueueURL = String @@ QueueURL.Marker

我是 Scala 的新手,所以我可能错了,但 @@(双符号)看起来不像是标准的内置 Scala 运算符。此外,细心的读者会发现它是从“scalaz”库导入的:

import scalaz.{ Tag, @@ }

@@ 有什么作用?为什么要使用它?

如 cmets 中所述,actual definition is:

 type @@[A, T] = A

这可能是一些提示。

【问题讨论】:

  • https://github.com/scalaz/scalaz 似乎没有提到它。
  • 定义为here

标签: scala scalaz


【解决方案1】:

标签的想法是,很多时候你不想在任何地方使用原始的Longs、Ints 等等——好吧,你想在你的代码中使用它们,但你不想在接口级别传递它们:

def fetchUsers(numberOfUsers: Int, offset: Int): Seq[User]

这里你必须使用命名参数来确保你没有交换参数的顺序。此外,有人可能会错误地用错误的顺序覆盖它:

override def fetchUsers(offset: Int, numberOfUsers: Int): Seq[User]

为避免这种情况,您可以对两个参数使用不同的类型。一种方法是使用带有AnyVal 的案例类 - 如果您遵循一些规则,它会被编译器优化为原语。标签是为一些常见类型(可能不一定是原语)引入新类型的替代方法。 @@ 定义为

type @@[A, T] = A

如你所见。所以你可以将新类型定义为:

@@[String, QueueURL.Marker]

但是因为 Scala 允许我们在类型上使用中缀语法,所以我们也可以将其写成:

String @@ QueueURL.Marker

如果您使用例如进行依赖注入,这将特别有用。隐含或 Macwire - 参数仅基于类型获取,因此必须为每个可注入值具有区分类型(在旁注中,Macwire 实现了它自己的 @@ 版本 - 他们的做法略有不同,但它目的相同)。

然后你可以得到这样的代码:

def fetchUsers(numberOfUsers: Int @@ UsersNumber, offset: Int @@ Offset): Seq[User]

或:

type UsersNumber = Int @@ UsersNumberTag
type UsersOffset = Int @@ UsersOffsetTag

def fetchUsers(numberOfUsers: UsersNumber, offset: UsersOffset): Seq[User]

我也看到了这个变种:

type UsersNumber[T] = T @@ UsersNumberTag
type UsersOffset[T] = T @@ UsersOffsetTag

def fetchUsers(numberOfUsers: UsersNumber[Int], offset: UsersOffset[Int]): Seq[User]

查看 aws-scala 代码,我假设他们想要实现第一个属性 - 能够区分某些常见类型的不同用法(如 String)并使用编译器检查它们是否没有出错。

菊石中的例子:

@ type @@[A, T] = A with T
defined type @@

@ trait Username
defined trait Username

@ def passUsername(username: String @@ Username): Unit = ()
defined function passUsername

@ passUsername("test")
cmd3.sc:1: type mismatch;
  String|String with Username
val res3 = passUsername("test")
                        ^
Compilation Failed

【讨论】:

  • 能否请您对上述示例进行更改,使用标签帮助避免您提到的错误
  • 能否在使用错误类型时也显示编译错误?
  • 已添加,但以防万一:现在是 2020 年,如果您需要使用标记类型提供的功能,您可能应该使用 @newtype 或者如果您使用的是 Dotty opaque types
【解决方案2】:

标记使创建新类型变得容易。它使用@@ 符号将现有类型“标记”为另一种类型(换句话说,它创建了一个新类型)。所以 String @@ Text 应该读作“用文本标记的字符串”。不知道为什么要使用它。

【讨论】:

  • “标记”是什么意思?像包装一样 - 类似于 Option 所做的事情? (从类型的角度来看)
  • @GrzegorzOledzki 标记意味着赋予类型特殊的含义。
猜你喜欢
  • 2021-02-06
  • 2015-10-29
  • 1970-01-01
  • 2014-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-11
相关资源
最近更新 更多