2015-02-18 21 views
15

programiści Scala mieć kilka opcji przy określaniu wyliczeń:Koncepcja liczenia w Scali - którą opcję wybrać?

  1. Stosować Scala Enumeration
  2. Mimic wyliczenia przy użyciu Scala zamknięte obiekty przypadków.
  3. Zastosowanie Scalaz Enum
  4. Zastosowanie Java Enumeration

Podczas badania najlepszych praktyk na całym wyliczeń w Scala, natknąłem się na stanowisko Google zatytułowanej Enumerations must DIE a także this blog, który zwraca uwagę na potencjalny problem przy użyciu klasy Scala Enumeration. Oba te odniesienia rzuciły negatywny cień na klasę Scala Enumeration.

Opcja 2 wydaje się być bardzo pracochłonna iw odniesieniu do Opcji 3, nie korzystałem jeszcze z biblioteki Scalaz, więc chciałbym poznać doświadczenia, jakie mieli inni przy korzystaniu z ScalazEnum. Ostatnią opcją jest inter-op z Javą, której staram się unikać, ponieważ lubię podejmować purystyczne podejście w programowaniu Scali.

Celem tego posta jest wykorzystanie doświadczenia społeczności w celu uszczegółowienia kontekstu (ów), gdy jedna opcja byłaby preferowana w stosunku do innej, a także w tym kontekście, w których dany konkretny wariant byłby niewłaściwy lub powodują poważne problemy, tak aby można było podjąć świadomą decyzję przy wyborze jednej opcji zamiast drugiej. Nie szukam opinii, ale raczej konkretnego kontekstu (ów) użycia, gdy jedna opcja jest lepsza od drugiej (innych); opinie mogą spowodować zamknięcie tego postu, więc należy tego unikać.

+0

Mam nadzieję na to pytanie. Wydaje się, że powinna istnieć/musi być ostateczna odpowiedź, dla której należy zastosować podejście enum. Niedawno odkryłem raj enum, ale nie próbowałem go, więc rzuciłem go na ring. Uważam, że to pytanie jest ważne, ponieważ przedstawia listę opcji, a nie pytanie o zalecenia. – acjay

+0

Istnieje również sedno Viktora Klanga (https://gist.github.com/viktorklang/1057513) i sedno chaosu3 (https://gist.github.com/chaotic3quilibrium/57add1cd762eb90f6b24). Sądzę, że ta ostatnia ma na celu udoskonalenie tego pierwszego z wyczerpującą kontrolą stwierdzeń "meczowych". – acjay

+0

Napisałem mały przegląd o Wyliczeniach scala i alternatywach, może się okazać, że jest to przydatne: http://pedrorijo.com/blog/scala-enums/ – pedrorijo91

Odpowiedz

3

Użyłem obu z dwóch pierwszych opcji w przeszłości w zależności od okoliczności. Nie mogę mówić o innych opcjach, ale nie chciałbym używać Java Enums. Generalnie zawsze wolę rozwiązanie Scala niż rozwiązanie Java, w którym jest ono dostępne. Byłbym też niechętny do wprowadzenia biblioteki tylko dla Wyliczeń. Wydaje się nieco trudne, aby wprowadzić dużą bibliotekę do wykonania tak małego zadania, zwłaszcza gdy są wbudowane w sposób umożliwiający wykonanie tego zadania. Może być inaczej, jeśli biblioteka oferuje inne funkcje, które chciałem, a które nie zostały wbudowane.

Część tego zależy od tego, czego potrzebujesz do wyliczenia. Jeśli potrzebujesz go po prostu do stworzenia zestawu dyskretnych wartości, to skłaniałbym się ku Opcji 2. Naprawdę nie jest to dużo pracy. Można uczynić go bardziej skomplikowane, jeśli twoje potrzeby wymagają, ale najbardziej podstawowym scenariuszem jest:

trait MyEnum 
case object MyValue1 extends MyEnum 
case object MyValue2 extends MyEnum 

Jeśli z drugiej strony trzeba coś, co faktycznie daje Ci „na zamówienie” zestaw dyskretnych wartości, które można iteruj, uzyskuj wartości liczbowe dla etc, wtedy mogę bardziej pochylić się nad wyliczaniem Scala.

+0

Proszę rozszerzyć swój wpis, aby uwzględnić, dlaczego nie chcesz używać Javy Enums i biblioteki 3rd party. Pomogłoby to innym w podejmowaniu świadomych decyzji. Dziękuję Ci. –

+0

Dodano żądane wyjaśnienia. – Wade

+1

Dwie pomniejsze sugestie: (1) użyj "zapieczętowanej cechy", (2) zawijaj wszystkie obiekty sprawy w obiekcie towarzyszącym o tej samej nazwie co cecha. Imho skutkuje piękniejszym scopingiem, np. 'MyEnum.Value1' i zazwyczaj pozwala na użycie krótszych nazw dla wartości, ponieważ nie zanieczyszczasz zasięgu nadrzędnego (np.' MessageType.Ping' zamiast mówić 'PingMessageType') . – bluenote10

7

Scala Wyliczanie

Zaletą teksty stałe Scala, że ​​są lekkie. Jeśli chcesz tylko zestawu uporządkowanych wartości, będzie to najlepszy wybór. Są jednak dwa wady. Po pierwsze, trudno jest dodać do nich zachowanie; ich głównym celem jest istnienie jako unikalna instancja, a nie zapewnienie złożonej funkcjonalności. Po drugie mają wymazanie typów. Twój drugi link pokazuje, jak metoda przeciążania nimi nie działa.

szczelnym opakowaniu Przedmioty

przypadku obiekty to drugi koniec widma. Każda z nich jest własną klasą - co oznacza brak wymazywania typu - i może zapewnić unikalne, złożone zachowanie. Wady są wysokie, ponieważ każdy jest własną klasą i brak wbudowanej iteracji nad nimi. Jest to dobry wybór, jeśli chcesz określić unikalne pola/metody/implementacje dla niektórych lub wszystkich instancji. One również nadają się dobrze do match s, ale nie do iteracji we wszystkich przypadkach.

Java Wyliczanie

Coś w średnim ziemi. Dodawanie metod/pól do samego wyliczenia jest łatwe, ale dostosowywanie zachowania poszczególnych instancji jest znacznie trudniejsze. Wszystko jest zawarte w jednej klasie, więc ma mniej narzutów niż obiekty typu case i nie ma takiego samego problemu z wymazywaniem typów jak w Scala. Jeśli posiadasz uporządkowaną, iterowalną listę wartości jest ważna i potrzebujesz dodatkowej funkcjonalności, która jest wspólna dla wszystkich instancji, te działają dobrze. Są także użyteczne, jeśli usunięcie typu byłoby problemem (np. Jeśli planujesz wysłać do funkcji opartej na typie). Wreszcie zapewnią trywialne współdziałanie z Javą.

Nie użyłem żadnej z bibliotek, o których wspomniałeś, nie powiem o nich nic.

Podsumowanie

ScalaEnum

  • iterowalny
  • niski narzut

Sealed Case Przedmioty

  • Unikalne pola i metody na przykład

Java Enum

  • łatwo wdrożyć wspólnych pól i metod
  • iterowalny współdziałanie
  • Łatwy Java
  • niski narzut
3

Scalaz Enum

  • Wyliczanie koncepcja Scalaz jest wzorowany na Haskell Enum
  • dostarcza użytecznych operacji na sekwencyjnie zamówione rodzaje
  • Develop bogate wyliczeń przez zapieczętowanych klas przypadków
  • Iterate nad tymi zamknięte klasy obudów w sposób bezpieczny dla typów
  • Wymagana jest większa analiza ze strony ne pojęcie porządku dla typu, który ma być wyliczony.
  • niezbędny do zdefiniowania succ i pred funkcje
  • wymagane do przesłonić funkcję order z klasy Order typu.

Przykład

import scalaz.Ordering.{EQ, GT, LT} 
import scalaz.{Enum, Ordering, Show} 

sealed abstract class Coloring(val toInt: Int, val name: String) 

object Coloring extends ColoringInstances { 

    case object RED extends Coloring(1, "RED") 

    case object BLUE extends Coloring(2, "BLUE") 

    case object GREEN extends Coloring(3, "GREEN") 

} 

sealed abstract class ColoringInstances { 

    import Coloring._ 

    implicit val coloringInstance: Enum[Coloring] with Show[Coloring] = new Enum[Coloring] with Show[Coloring] { 

    def order(a1: Coloring, a2: Coloring): Ordering = (a1, a2) match { 
     case (RED, RED) => EQ 
     case (RED, BLUE | GREEN) => LT 
     case (BLUE, BLUE) => EQ 
     case (BLUE, GREEN) => LT 
     case (BLUE, RED) => GT 
     case (GREEN, RED) => GT 
     case (GREEN, BLUE) => GT 
     case (GREEN, GREEN) => EQ 
    } 

    def append(c1: Coloring, c2: => Coloring): Coloring = c1 match { 
     case Coloring.RED => c2 
     case o => o 
    } 

    override def shows(c: Coloring) = c.name 

    def zero: Coloring = Coloring.RED 

    def succ(c: Coloring) = c match { 
     case Coloring.RED => Coloring.BLUE 
     case Coloring.BLUE => Coloring.GREEN 
     case Coloring.GREEN => Coloring.RED 
    } 

    def pred(c: Coloring) = c match { 
     case Coloring.GREEN => Coloring.BLUE 
     case Coloring.BLUE => Coloring.RED 
     case Coloring.RED => Coloring.GREEN 
    } 

    override def max = Some(GREEN) 

    override def min = Some(RED) 

    } 

} 

Przykład wyjściowa:

val f = Enum[Coloring] 
println(f.fromToL(Coloring.RED, Coloring.GREEN)) 

res1 : List(RED, BLUE, GREEN) 
+1

Należy zauważyć, że ten kod wymaga niewielkiej zmiany, aby rzeczywiście korzystać z dodatkowych funkcji, takich jak '| ->', 'succ',' - + - '. Jeśli używasz: 'klasa przypadek Farbowanie (val toInt: Int, nazwa val: String) obiekt Farbowanie rozciąga ColoringInstances { val RED = barwiące (1, "czerwony") val niebieski = kolorowanie (1,„Niebieska ") val GREEN = Kolorowanie (1," ZIELONY ") } ' Scalaz znajdzie prawidłowe implikacje dla dodanych funkcji. Pełny przykład można znaleźć tutaj: http://www.smartjava.org/content/scalaz-features-everyday-usage-part-1-typeclasses-and-scala-extensions –

2

Można również użyć Enumeratum. Opis pochodzi z ich dokumentacją:

Enumeratum jest realizacja typu bezpieczne i wydajne wyliczenie dla Scala, który oferuje wyczerpujące ostrzeżenia wzór meczu, integracje z popularnych bibliotek Scala i idiomatyczne wykorzystanie że nie złamie IDE .Ma na celu być na tyle podobnym do Scala wbudowany w wyliczenie do być łatwy w użyciu i zrozumieć, oferując więcej elastyczności, wartości bezpieczeństwa i bogatsze wyliczanie bez konieczności utrzymywania własnego zbioru wartości.

Powiązane problemy