Enum in Scala

@hakaicode

As a Result

sealed abstract class Status(val code: Int)
object Status {
  case object OK extends Status(0)
  case object NG extends Status(1)
  case object RESERVE extends Status(2)
  case object NA extends Status(9)

  val values = Vector(OK, NG, RESERVE, NA)
  def withName(s: String): Option[Status] = values.find(_.toString()==s)
  def withCode(c: Int): Option[Status] = values.find(_.code==c)
}

I have to use int numbers for DB records like '0','1','2'...

It's old technology.

We should use the name of enums like 'OK','NG'...

First Question

Which is the best practice for Scala Enum?

I was supposed to talk about the rank of search result

Result of google Japanese version is so nice.

1.xerial.org/scala-cookbook

object DNA {
  // objectで定義するとsingletonになる
  case object A extends DNA(0)
  case object C extends DNA(1)
  case object G extends DNA(2)
  case object T extends DNA(3)
  case object N extends DNA(4)

  // DNAの文字列をすべて並べる。
  val values = Array(A, C, G, T, N)
  // 用途によって別の集合を定義することもできる
  val exceptN = Array(A, C, G, T)

  private val codeTable = Array(A, C, G, T, N, N, N, N)

  def complement(code:Int) : DNA = codeTable((~code & 0x03) | (code & 0x04))
}

// sealedを付けると、DNAを拡張したクラスはこのファイル内でしか定義できない
// abstractを付けると、DNAを拡張したクラスはA, C, G, T, N以外にないことを保証できるので
// match文がexhaustive(すべてのケースを網羅)になる
sealed abstrat class DNA(val code:Int) {
    // A, C, G, T, Nをcase objectとすると、クラス名を表示するtoStringが実装される
    val name = toString
    // DNAクラスには自由にメソッドを定義できる
    def complement = DNA.complement(code)
}

Useful but..

Unusable example. I always need like 'withName()' method.

def complement(code:Int) : DNA = codeTable((~code & 0x03) | (code & 0x04))

There are so many comments, I couldn't understand smoothly.

They are acceptable, the problem is next.

2.http://qiita.com/suin/

// Attenderエンティティ
case class Attender(name: String, status: Attender.Status = Attender.Status.Maybe) {
    def attend = copy(status = Attender.Status.Going)
    def absent = copy(status = Attender.Status.NotGoing)
}

object Attender {
    // 列挙型の定義はここ
    sealed abstract class Status
    object Status {
        case object Going extends Status
        case object NotGoing extends Status
        case object Maybe extends Status
    }
}

val attender = Attender("Hidehito Nozawa")

println(attender)
println(attender.attend)
println(attender.absent)

It bothered me..

  • The writer explain with sample code not only enums, but It made me more difficult to figure out that enum should be written.
  • Does he use enum from one class? I confused.

「実際のコードでは、列挙型を使うクラスのコンパニオンオブジェクトの一部にしたほうが、整理されていていいと思う。」

  • This page is googled upper than xerial.org/scala-cookbook and he quoted it. So I thought this is improved way of cookbook.

Second Question

 def withName(s: String): Option[Status] = values.find(_.toString()==s)

Everytime finding in loop is not fast.

But it is not smart writing HashMap cache for every enum making.

  val cache = HashMap[String,Status]()
  def withName(s:String) = cache.getOrElse(s, find(_))
  private[this] find(s:String) = { 
    val r = values.find(_.toString()==s))
    cache.put(s.r)
    r
  }

For need efficiency, write cache like this?

Mutable HashMap or make all in initializer?

※Scala doesn't have Map#computeIfAbsent

Third Question

Which collection class I should use for enum values?

迷ったらVector

Fine.

Made with Slides.com