scala ADT через запечатанные черты - есть ли способ десериализации из строки обычным способом

Допустим, у меня есть следующая черта

trait Named {
  def name: String
}

и следующий алгебраический тип данных

sealed trait Animal extends Named

case object Dog extends Animal {
  override val name: String = "dog man"
}

case object Cat extends Animal {
  override val name: String = "cat man"
}

case object Owl extends Animal {
  override val name: String = "I am an owl left in the dark"
}

Теперь я могу десериализовать экземпляр строки в свой Animal ADT с помощью следующего метода.

object Animal {

  def apply(name: String): Animal = name match {
    case Dog.name => Dog
    case Cat.name => Cat
  }
}

@oxbow_lakes упоминает в конце своего ответа, что:

Невозможно легко создать экземпляр из сохраненного значения. Это также верно, но, за исключением случая огромных перечислений (например, всех валют), это не вызывает огромных накладных расходов.

Я считаю, что тот факт, что когда вы добавляете новое значение, его необходимо добавить в код десериализации явно как подверженный ошибкам (я думал, что компилятор предупредит меня о неполном совпадении, но взгляните на Owl выше и apply метод - предупреждения не выдавалось ...)

Нет лучшего способа? (Если не со стандартным набором инструментов scala, сторонним?)


person Yaneeve    schedule 27.04.2017    source источник
comment
Todo для меня, ответьте на мой собственный вопрос. Нашел решение на основе двух сообщений: stackoverflow.com/questions/25838411/ и stackoverflow.com/questions/43650265/ в сумме: stackoverflow.com/a/43658710/101715   -  person Yaneeve    schedule 07.05.2017


Ответы (2)


Эта проблема уже решена enumeratum библиотекой: https://github.com/lloydmeta/enumeratum

Ваш код можно было бы написать так:

import enumeratum._
import enumeratum.EnumEntry.Lowercase

sealed trait Animal extends EnumEntry with Lowercase
object Animal extends Enum[Animal] {
  val values = findValues

  case object Dog extends Animal
  case object Cat extends Animal
  case object Owl extends Animal
}

val dogName = Animal.Dog.entryName
val dog = Animal.withNameInsensitive(dogName)
person user1303559    schedule 27.04.2017

Вы можете попробовать использовать отражение для получения набора типов, расширяющих Animal, затем использовать это для создания Map[String,Animal], используя name для поиска значений объекта, а затем использовать карту в вашей Animal.apply функции.

Обратитесь к этот вопрос для получения дополнительной информации о получении Animal подклассов.

person Mike Allen    schedule 27.04.2017
comment
Это сработает, но я бы предпочел способ сделать это без размышлений, если это возможно. - person Yaneeve; 27.04.2017
comment
Просто из любопытства, почему вы предпочитаете избегать размышлений? Вам нужно будет использовать его только один раз, чтобы настроить карту String на Animal, чтобы это не повлияло на производительность и не пропустило ни одного Animal подкласса. - person Mike Allen; 27.04.2017
comment
Очевидно, что ваша оценка верна в отношении правильности и эффективности. Я думаю, что причина, по которой мне было бы лучше с решением для времени компиляции, заключается в том, что мне кажется, что должен быть способ сделать это. sealed traits сообщить компилятору, что никакого расширения времени выполнения не будет, что означает, что все известные прямые подтипы известны заранее. Я думаю, что эти знания нужно использовать. Не знаю, как это сделать, поэтому и разместил вопрос. Интересно, можно ли сделать то, что я хочу, с помощью бесформенных или макросов. IMHO, Вообще то, что можно сделать во время компиляции, должно - person Yaneeve; 30.04.2017
comment
Вы можете использовать макросы с отражением, чтобы добиться этого во время компиляции. По сути, это то, что делает библиотека enumeratum. Обратной стороной макросов является то, что они должны быть в отдельном (подпроекте). То есть, если вы используете SBT, вы должны создать один подпроект, который определяет макросы (для запроса запечатанных типов и создания sets, содержащих все подклассы), и другой подпроект (который зависит от подпроекта макроса), который содержит классы case и сохраненную карту поиска. Это не должно быть слишком сложно. Я использовал только HList функцию Shapeless, поэтому не могу комментировать, как это может помочь. - person Mike Allen; 30.04.2017