Scala-wyliczenie a Case-klasy

Stworzyłem aktora akka o nazwie LogActor. Logaktorzy odbierają metody obsługi wiadomości od innych aktorów i rejestrują je do określonego poziomu dziennika.

Mogę rozróżnić różne poziomy na dwa sposoby. Pierwszy:
import LogLevel._
object LogLevel extends Enumeration {
    type LogLevel = Value
    val Error, Warning, Info, Debug = Value
}
case class LogMessage(level : LogLevel, msg : String) 

Drugi: ( EDIT )

abstract class LogMessage(msg : String)
case class LogMessageError(msg : String) extends LogMessage(msg)
case class LogMessageWarning(msg : String) extends LogMessage(msg)
case class LogMessageInfo(msg : String) extends LogMessage(msg)
case class LogMessageDebug(msg : String) extends LogMessage(msg)
Która droga jest bardziej efektywna? czy dopasowanie wielkości liter lub wartości enum zajmuje mniej czasu?

(przeczytałem to pytanie, ale nie ma żadnej odpowiedzi odnoszącej się do runtime issue)

Author: Community, 2012-11-12

3 answers

Obowiązuje"Nie Optymalizuj przedwcześnie". Nie wierzę, że różnica między nimi w ogóle ma znaczenie, w porównaniu z czasem, który spędzasz przekazując Wiadomości do swojego aktora lub faktycznie je rejestrując. Ale spodziewam się, że najlepszą wydajnością będzie stworzenie Javy enum (która może być łatwo dostępna i używana ze Scali) do logowania poziomów zamiast Scali Enumeration.

 7
Author: Alexey Romanov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-12 13:29:56

Całkowicie zgadzam się z Alexeyem i Dennisem, że wydajność w tym przypadku nie powinna ci przeszkadzać, ponieważ jest to bardziej problem optymalizacji kompilatorów, a nie programistów i nie wyobrażam sobie scenariusza, w którym różnica w wydajności mogłaby stać się zauważalna.

To, co powinno ci przeszkadzać, to spójność kodu i w tym sensie powinieneś oprzeć swoją decyzję na tym, czy chcesz trzymać się starego podejścia Java-owskiego z enumami, które jest poprawnie opisane w Twoim pierwszym przykładzie, czy ostatnio coraz popularniejszy wzór algebraicznych typów danych (ADT). Ten ostatni próbowałeś reprezentować w swoim drugim przykładzie, ale z pewnymi błędami.

Poniżej znajduje się sposób poprawnego rozwiązania problemu za pomocą wzorca ADT.

ADT Solution # 1

// 1. marked `sealed` to make pattern matching exhaustive
// 2. used a trait to avoid double storage of msg` and 
//    make the inheritance easier
sealed trait LogMessage { def msg : String }
// A better solution for isolation than names like "LogMessageError".
// Allows you to either address the members with a namespace like 
// "LogMessage.Error" or do "import LogMessage._" and address them 
// directly
object LogMessage { 
  case class Error (msg : String) extends LogMessage
  case class Warning (msg : String) extends LogMessage
  case class Info (msg : String) extends LogMessage
  case class Debug (msg : String) extends LogMessage
}

ADT Solution # 2

Przepraszam, że pewnie mieszam Ci w głowie, ale warto zauważyć, że istnieje również alternatywne podejście ADT do podobnych sytuacji, które jest trochę podobne do tego, które kierujesz z enumami.

sealed trait LogLevel 
object LogLevel {
  case object Error extends LogLevel
  case object Warning extends LogLevel
  case object Info extends LogLevel
  case object Debug extends LogLevel
}
case class LogMessage ( level : LogLevel, msg : String )
 26
Author: Nikita Volkov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-12 16:58:07

Jedną z najczęstszych operacji dla loggera jest porównanie bieżącego poziomu logowania do poziomu wiadomości, a dzięki enums otrzymujesz go za darmo, podczas gdy przy konfiguracji z klasami przypadków będzie to nieco kłopotliwe. I zgadzam się z @ AlexeyRomanov: dopasowanie nie powinno być tu wąskim gardłem.

EDIT: pod względem wydajności, match with case classes użyje instanceof w bajtowym kodzie, podczas gdy dla enums kompilator Scala wygenerował kod, z którym żaden z dekompilatorów nie był w stanie sobie poradzić. Informatyka wydawało się, że używa equals. Więc technicznie wyliczenia mogą być szybsze, ale w rzeczywistości nie będzie różnicy w wydajności.

 0
Author: Denis Tulskiy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-12 15:03:31