0

I think I had a good understanding of Scala till I find myself in this simple scenario

sealed abstract case class Name private(name: String)

object Name {
    def make(name: String): Option[Name] =
      if (name.nonEmpty) Some(new Name(name) {}) else None
  }

my question is about the private modifier for the class. If I use it like this, everything works, but if I move the private keyword at the start, something like

private sealed abstract case class Name(name: String) it doesn't compile becuase gives me the following error

private class Name escapes its defining scope as part of type Option[example.package.Name]

where example.package is the package object I'm working in.

I think I need some clarification because I'm not sure what's happening here

Tizianoreica
  • 2,142
  • 3
  • 28
  • 43
  • 1
    Hint: *what* is the keyword in front of? – Jörg W Mittag Dec 15 '21 at 10:50
  • @JörgWMittag `what` is not a keyword in scala, not sure what you're talking about – Tizianoreica Dec 15 '21 at 10:52
  • 1
    Jörg meant: Where is the keyword located? In front of what? This is a hint for you so that you can find the answer to your question. – Suma Dec 15 '21 at 11:16
  • Does this answer your question? [Private and protected constructor in Scala](https://stackoverflow.com/questions/1730536/private-and-protected-constructor-in-scala) – Suma Dec 15 '21 at 11:18

2 Answers2

2

In

sealed abstract case class Name(name: String)

Name effectively denotes both

  • the name of the class
  • the default constructor for that class
sealed abstract case class Name private(name: String)

declares that the class is public, but the constructor is private (i.e. can only be called from within that class or its companion object).

private sealed abstract case class Name(name: String)

declares that the class is private (which implies that the constructor is also private).

Levi Ramsey
  • 18,884
  • 1
  • 16
  • 30
  • It is worth adding that you can still create instances of `case class private Name` outside the companion object because there is an automatic `public` constructor for a `case class`. Private constructors only really make sense for bare `class` definitions. – Tim Dec 15 '21 at 11:23
  • 2
    `sealed abstract case class` with a `private` constructor suppresses the auto-generated public `apply` method in the companion object. It's a useful trick for getting [a useful subset of] `case class` semantics while enforcing invariants without throwing exceptions (e.g. to have the `apply` method return `Option`/`Try`/`Either`/Cats `Validated`/etc.). – Levi Ramsey Dec 15 '21 at 11:27
  • 1
    Thanks for the clarification and apologies for the confusion. I was talking about non-abstract case classes which do have this issue, but that is clearly not a problem in this particular case. I have updated my own answer appropriately. – Tim Dec 15 '21 at 14:02
0

This code compiles with no error:

 private sealed abstract case class Name(name: String)

The problem is that you are then returning a value that contains a public value of this class, so the definition is leaking out and it isn't really private. You need to make sure that all references to Name are also private.

The first syntax marks the constructor as private;

sealed abstract case class Name private(name: String)

This means that the constructor can only be called from inside the Name companion object. So this is not allowed

val name = new Name("name") // constructor Name in class Name cannot be accessed
Tim
  • 26,753
  • 2
  • 16
  • 29
  • 2
    That is incorrect. Marking a `case class` `abstract` suppresses the companion `apply` (and the `copy` method). – Levi Ramsey Dec 15 '21 at 11:28
  • While what I said is technically not incorrect (since I said `case class` rather than `abstract case class`) it certainly was confusing, for which I apologise. I have remove this from my answer but I am leaving the rest of the answer here as it explains the error the OP is seeing whereas the other answer does not. – Tim Dec 15 '21 at 14:01