класс верхнего уровня без компаньона может развиваться только в одноименный класс или в блок, состоящий из одноименных спутников - PullRequest
1 голос
/ 20 июня 2020

Я пытаюсь использовать https://github.com/estatico/scala-newtype следующим образом:

import io.estatico.newtype.macros.newtype
import cats._
import io.databaker.env._

@newtype case class DbUrl(v: String)

@newtype case class DbUser(v: String)

@newtype case class DbPw(v: String)

final case class DbParams(url: DbUrl, user: DbUser, pw: DbPw)

trait DbConnector[F[_]] {
  def read(url: DbUrl, user: DbUser, pw: DbPw): F[DbParams]
}


object DbConnector {

  def impl[F[_] : MonadError[*[_], Throwable]](env: Environment[F])
  : DbConnector[F] =
    new LiveDbConnector[F](env)

}

, и компилятор жалуется:

[error] ../db/DbConnector.scala:7:2: top-level class without companion can only expand either into an eponymous class or into a block consisting in eponymous companions
[error] @newtype case class DbUrl(v: String)
[error]  ^
[error] ../db/DbConnector.scala:9:2: top-level class without companion can only expand either into an eponymous class or into a block consisting in eponymous companions
[error] @newtype case class DbUser(v: String)
[error]  ^
[error] ../db/DbConnector.scala:11:2: top-level class without companion can only expand either into an eponymous class or into a block consisting in eponymous companions
[error] @newtype case class DbPw(v: String)
[error]  ^
[error] ../env/Environment.scala:8:2: top-level class without companion can only expand either into an eponymous class or into a block consisting in eponymous companions
[error] @newtype case class EnvValue(v: String)
[error]  ^
[error] ../env/Environment.scala:6:2: top-level class without companion can only expand either into an eponymous class or into a block consisting in eponymous companions
[error] @newtype case class EnvVariable(v: String)  

Содержимое build.sbt :

lazy val root = (project in file("."))
  .enablePlugins(JettyPlugin)
  .settings(
    organization := "io.example",
    name := "user-svc",
    version := "0.0.1-SNAPSHOT",
    scalaVersion := "2.13.2",
    mainClass := Some("io.example.Main"),
    containerPort := 9090,
    libraryDependencies ++= Seq(
      "org.http4s" %% "http4s-servlet" % Http4sVersion,
      "org.http4s" %% "http4s-circe" % Http4sVersion,
      "org.http4s" %% "http4s-dsl" % Http4sVersion,
      "io.circe" %% "circe-generic" % CirceVersion,
      "org.scalameta" %% "munit" % MunitVersion % "test",
      "ch.qos.logback" % "logback-classic" % LogbackVersion,
      "io.estatico" %% "newtype" % NewTypeVersion,
      "javax.servlet" % "javax.servlet-api" % ServletVersion % "provided"
    ),
    addCompilerPlugin("org.typelevel" %% "kind-projector" % "0.11.0" cross CrossVersion.full),
    addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1"),
  )

scalacOptions ++= Seq(
  "-deprecation",
  "-encoding", "UTF-8",
  "-language:higherKinds",
  "-language:postfixOps",
  "-feature",
  "-Xfatal-warnings",
  "-Ymacro-annotations"
)

Что я делаю не так?

Обновление

Я переместил макросы newtype в объект пакета следующим образом:

package object db {
  
  @newtype case class DbUrl(v: String)

  @newtype case class DbUser(v: String)

  @newtype case class DbPw(v: String)

}

но компилятор все равно жалуется:

implicit conversion method opsThis should be enabled
[error] by making the implicit value scala.language.implicitConversions visible.
[error]   @newtype case class DbUser(v: String)

1 Ответ

3 голосов
/ 20 июня 2020

README.md of scala -newtype говорит:

Это расширяется до определения типа и сопутствующего объекта, поэтому новые типы должны быть определены в объекте или объекте пакета.

Макросам разрешено расширять классы в другие классы с тем же именем и сопутствующие объекты, но, насколько я могу судить, аннотация newtype превращает ваш case class в объект с таким же именем (вместе с псевдонимом типа, например type DbUrl = DbUrl.Type). Такое поведение (превращение аннотации верхнего уровня в дерево какого-либо другого типа) недопустимо. Если бы аннотация сгенерировала класс DbUrl и, возможно, объект с таким же именем, все было бы хорошо, но почти все остальное не сработает.

Чтобы исправить вашу проблему, все, что вам нужно сделать, это переместить это в объект пакета (или в какую-либо другую область, если он не является верхним уровнем).

Изменить: как указал Дмитрий Митин, созданный тип не является тип DbUrl, а что-то вроде type DbUrl = DbUrl.Type с заглавной буквой «T», где определение DbUrl.Type выглядит примерно так (я просто копирую это из README):

type Base = Any { type DbUrl$newtype }
trait Tag extends Any
type Type <: Base with Tag
...