【问题标题】:How to tell to the compiler a function can only return subtype of a trait and not the trait itself?如何告诉编译器一个函数只能返回一个特征的子类型而不是特征本身?
【发布时间】:2021-12-21 04:07:04
【问题描述】:

我正在尝试编写一些获取 URL、从该 URL 获取内容并保存它的代码。从 URL 中获取内容的方法是特定于网站的,但是提取的内容使用通用格式,例如可以保存在数据库中。

所以我首先定义一个代表每个支持的 URL 的类型:

trait Url(url: String)

case class Soundcloud(url: String) extends Url(url)
case class Youtube(url: String) extends Url(url)

然后一个给定字符串的方法返回正确的 URL 类型:

def matchUrl(s: String): Option[Url] =
  val s_regex = "soundcloud.com".r
  var y_regex = "youtube.com".r
  s match
    case s_regex() => Some(Soundcloud(s))
    case y_regex() => Some(Youtube(s))
    case _         => None

然后我为内容提取器定义一个特征:

trait GetContent[T <: Url]:
  def get: Unit  // should actually return something like `Content`

这是我在数据库中保存 URL 内容的方法:

def save[T <: Url](t: T)(using gc: GetContent[T]) =
  gc.get
  // save into db

然后我提供GetContent 的两种实现,每种URL 类型一种:

given GetContent[Soundcloud] with
  def get = println("getting some content from soundcloud")

given GetContent[Youtube] with
  def get = println("getting some content from youtube")

最后我可以将它们全部连接在一起:

matchUrl("youtube.com").map(r => save(r))

编译器抱怨

没有为方法 save 的参数 gc 找到类型为 GetContent[Url] 的隐式参数

如果我这样调用我的方法,它可以正常工作:

save(Soundcloud("http://soundcloud.com/some/content"))

我需要告诉编译器matchUrl 只能返回SoundcloudYoutube 的实例,而不能返回Url

【问题讨论】:

  • 你可以让自己的枚举模仿Option,但有一个额外的字段包含GetContent 隐含。不过,它非常不令人满意(至少我最终实现它的方式)。 Scastie。 (有趣的是,我的 Scastie 揭示了编译器中关于多态函数的几个错误/实现限制。看起来我们必须等待它被完善)

标签: scala implicit scala-3


【解决方案1】:

解决问题的一种方法是利用transparent inline。 此外,如果您想知道该方法最终将返回一个 Soundcloud 实例一个 Youtube 实例,您可以使用联合类型 |

在这种情况下,编译器使用子类型 return 细化返回类型:


transparent inline def matchUrl(s: String): Option[Soundcloud | Youtube] =
  inline s match
    // I use some regexs here for the real implementation
    case "soundcloud.com" => Some(Soundcloud(s))
    case "youtube.com"    => Some(Youtube(s))
    case _                => None

这样调用matchUrl("soundcloud.com")会返回精炼后的类型Option[Soundcloud],然后编译器就可以相应地找到隐式了。

Scastie snippet

【讨论】:

  • 好的,这适用于我的示例,因为编译器似乎可以使用静态字符串来知道匹配将返回哪种类型。但是我的真实用例使用的是正则表达式,编译器无法再推断匹配返回类型。
  • 哦,我不知道,对不起。是的,只有当编译器知道你在编译时传递了什么字符串时,这个解决方案才有效。如果您从用户输入中使用正则表达式,这将无法正常工作...
猜你喜欢
  • 1970-01-01
  • 2020-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-28
  • 1970-01-01
  • 2021-12-28
  • 2019-08-04
相关资源
最近更新 更多