Enum constructor from "extends" values

I have an enum like this

enum Draw(val char: Char):
  case Rock extends Draw('r')
  case Paper extends Draw('p')
  case Scissors extends Draw('s')
  // ... more consts like "case Well ..."

(The extends values are defined to read draws from text files.)

Now I need a “constructor” Char => Draw. My first implementation is:

def makeDraw(c: Char): Draw =
  Draw.values.find(d => d.char == c).getOrElse(Draw.Rock)```

This looks a bit convoluted, so I was thinking, maybe the companion object to Draw contains some helper making this definition easier? This must be a common situation…

Personally I would make the code return an Option[Draw] since the char may not be one of the cases and defaulting to an arbitrary one feels wrong.
Also, not sure what is convoluted about that code, is literally just a one-liner and is very clear on its intent.

Anyways no, AFAIK, there is no other way using plain enum
You may check other libraries like enumeratum or enum-extensions.

1 Like

Thanks for chiming in!

Good point. Laziness usually backfires…

I’m glad you like my beginner’s code. :slight_smile:

With “convoluted” I just meant, if there is a built-in way to do this conversion for enums, then doing it manually feels unnecessarily complicated.

Finally, do you think I should put the makeDraw factory function into an companion object?

Imo, yes. I usually do sth like:

object Draw:
  def make(c: Char): Option[Draw] =
    Draw.values.find(d => d.char == c)
1 Like

Agree the companion object is the expected location. Also, it’s conventional for the method to be named either apply or fromChar.

2 Likes

Yeah, totally as Thanh and Seth said, that is the natural place.
Also, I would name it fromValue or fromChar.

So you are thinking of a very specific kind of enum; usually called value enum. If that was the only kind of enum the language supported, then sure this fromValue should be defined.
However, enum is very much general and supports a lot of ADTs, thus assuming all cases will have a single value and it will be a constant is not viable.
Thus, you rather have generic tools to build your desired behavior.

IMHO, the language should have two constructs, enum and adt, and enum should be very close to what Enumeratum provides. But that boat already sailed.

1 Like

How about an extension to Char that looks like a normal conversion to method? So we can use char.toDraw? Do people like that? :woozy_face:

extension (c: Char) def toDraw = c match
  case 'r' => Draw.Rock
  case 'p' => Draw.Paper
  case _   => Draw.Scissors

Personally, I try to avoid extension methods for types that I control.

fromChar – I love it. Thanks.

But it feels weird to me when a static method Draw.fromChar does not return a Draw but a Option[Draw]

That is actually a very common idiom in the language.
If the method does nothing fancy and returns a plain value of the companion type, then we usually use apply.
But, if the method performs some validations and what not and returns something like Option[Foo] or Either[Error, Foo] then fromX is a common name.

1 Like

I understand this now (see Scala 3 Enum vs Enumerations - #3 by philipschwarz) and I totally agree: To use enum to create Haskell-like ADTs in a concise manneris slightly weird.