Pull to refresh

Comments 8

Ненормальное программирование
Как здорово вы угадали с хабом.

Вообще, интересная вещь, но только если количество сущностей в проекте over 9000, иначе излишнее усложнение не окупается удобством того же рефакторинга.
Здесь мы сохраняем возможность рефакторинга. Все свойства представлены объектами, хранятся в именованных константах (val'ах). С точки зрения объявления «класса» получается не сильно больше, чем объявление обычного case class'а.
case class Person(
    name: String, 
    address: Address
)

vs.
object Person{
    val name = slot[String]("name")
    val address = slot[Address]("address")
}

да это понятно из статьи.

я про то, что использование подобного подхода имхо оправдано только на крупных проектах с большим количеством моделей, например.
Все сугубо, ИМХО. «Правильной дорогой идете товарищи» (с) Одна из проблем сегодняшнего программирования, то, что объектом является аристотелева вещь с фиксированным набором свойств. Вот один из примеров, почему это сильно усложняет программирование сложных систем.
Человек рождается с очень ограниченным набором свойств: иметь возраст, вес, рост, пищать, питаться и портить подгузник. Время идет, и он приобретает новые наборы свойств: ученик школы, покупатель, пассажир, солдат, студент, наемный работник, предприниматель, супруг, родитель и т.д. А возможно и не приобретает. Например, не каждый человек служит в армии, учится в вузе, женится и становится отцом или предпринимателем. Или утрачивает. Например, закончил учиться, отслужил в армии или развелся. Следовательно, один и тот же объект должен иметь возможность принадлежать разным классам и этот набор классов должен быть динамическим, т.е. изменяться в ходе эволюции объекта и самой программной системы. На набор классов, к которым относится объект, как правило, накладываются ограничения. Например. Чтобы стать солдатом, человек должен достичь 18 лет. А если человек студент то для того, чтобы стать мужем, необходимо сдать сопромат.

То, что вы описали, как я понял, похоже на путь к решению этой проблемы. Следующий шаг — осознать, что свойство != имя + значение, а является полноценным объектом, который проявляется во взаимодействии со свойствами других вещей.

Успехов.
HList напоминает Union Type. Наверное мне пора серьёзнее посмотреть на Shapeless.

Не совсем понятно применение. Экономии на количестве классов в JVM не получается, потому что «object Person» создаст класс «Person$». Чтобы избежать copy-paste на однотипных операциях можно использовать дженерики, хоть там type erasure и ограничивает возможности узнавать тип в рантайме. Но в паре с Typeclass pattern (см. например мой блог) можно это обойти, да ещё и перенести обработку выбора кода для конкретного типа на этап компиляции.

Пример с delta был бы интересен. Особенно чем он отличается от

case class Person(val name: String, val address: Option[String])
val john = Person("John", None)
val john2 = john.copy(address = Some("22b Baker st")
1. UnionType (by Miles Sabin) используется для того, чтобы сформировать объединённый тип всех свойств, которые уже использовались в текущем объекте

  type SlotsUnion = head.type with tail.SlotsUnion

(Здесь мы используем оператор with, который соответствует конъюнкции. Для проверки принадлежности мы инвертируем условие с помощью оператора <:<

def get[SlotType<:SlotId[_](slotId:SlotType)(implicit ev: SlotSeq1#SlotUnion <:< SlotType) = ???


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

3. Пример с delta. Пусть класс Person содержит 100 полей. И пусть среди этих полей есть свойство типа T: Numeric (например, Int, Double и т.п.). Тогда мы можем объявить специальный класс PropertyDelta, который несёт изменение этого свойства

  case class SlotDelta[T:Numeric, S <: SlotId[T]](slotId:S, delta:T)


Это может быть экономнее, чем копировать оставшиеся 99 полей (при сохранении в БД, при передаче по сети и т.п.). Кроме того, здесь мы явно видим, что именно изменилось, можем проверить допустимость изменения этого свойства согласно каким-либо правилам.

Теперь мы можем этот объект использовать для того, чтобы увеличить значение свойства на указанную величину:

  case class SlotDelta[T:Numeric, S <: SlotId[T]](slotId:S, delta: T) {
    def addTo(oldValue:SlotValue[T, S]) =
      SlotValue(slotId, implicitly[Numeric[T]].plus(oldValue.value, delta))
  }


Или немного поинтереснее:

  implicit class SlotValueEx[T:Numeric, S <: SlotId[T]] (slotValue:SlotValue[T, S]) {
    def +(delta:T) =
      SlotValue(slotValue.slotId, implicitly[Numeric[T]].plus(slotValue.value, delta))
  }

P.S. Вышеприведённый вариант — для общего случая, для Int, Double, BigNumber… и других T, для которых есть Numeric[T]. Если delta имеет известный тип, то можно и проще:
  case class SlotDelta[S <: SlotId[Int]](slotId:S, delta: Int) {
    def +:(oldValue:SlotValue[Int, S]) =
      SlotValue(slotId, oldValue.value+ delta)
  }

Спасибо за пояснения. Да, действительно интересный подход.
Sign up to leave a comment.

Articles

Change theme settings