Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save DanyMariaLee/2774f10e64e6aaff2e855061aa74a16c to your computer and use it in GitHub Desktop.
Save DanyMariaLee/2774f10e64e6aaff2e855061aa74a16c to your computer and use it in GitHub Desktop.
//Вопрос: А разве нельзя было то же самое на shapeless зарядить с автоматическим выводом тайп-классов?
/* в статье взят за пример гетерогенный лист, но это не единственный тип и не главная цель
использования данного подхода с макросами.
Самый простой случай, с которым мы все сталкиваемся,
имея дело с одинаковым поведением наследников
*/
// case one
trait Simple[T] {
def f(t: T): Unit = println(t)
}
def simpleDef[T](t: T)(implicit ev: Simple[T]): Unit = ev.f(t)
/* теперь, чтобы воспользоваться функцией simpleDef необходимо описать поведение для всех типов,
которые могут быть использованы, иначе получим такие ошибки (пример для строки)
*/
Error:(16, 12) could not find implicit value for parameter ev: Simple[String]
simpleDef("fff")
Error:(16, 12) not enough arguments for method simpleDef: (implicit ev: Simple[String])Unit.
Unspecified value parameter ev.
simpleDef("fff")
//нужно допистаь поведение (вариантов много, например такой)
object Simple {
implicit case object st extends Simple[String] {}
implicit case object si extends Simple[Int] {}
implicit case object sb extends Simple[Boolean] {}
implicit case object sbd extends Simple[BigDecimal] {}
implicit case object sd extends Simple[Double] {}
implicit case object sf extends Simple[Float] {}
implicit case object sc extends Simple[Char] {}
implicit case object ssh extends Simple[Short] {}
implicit case object sl extends Simple[Long] {}
//... в зависимости от фантазии
}
// теперь при помощи макросов сократим
object Simple {
implicit def materialize[T]: Simple[T] = macro materializeImpl[T]
def materializeImpl[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[Simple[T]] = {
import c.universe._
c.Expr[Simple[T]](q""" new Simple[${weakTypeOf[T]}] {} """)
}
}
/* Таким образом макросы сгенерят любые требуемые объекты (всех используемых в simpleDef типов)
самостоятельно. В случае, если описанное вами поведение создания такого объекта не сработает,
вы получите ошибку во время компиляции и сможете ее исправить. В макросах есть механизм дебагинга,
где можно увидеть конкретно с чем связана проблема.
После того, как вы все прописали и отладили - можно использовать, импортировав объект Simple.
*/
/*case two
про shapeless и auto typeclass derivation
возьмем простой случай (https://www.scala-exercises.org/shapeless/auto_typeclass_derivation)
*/
trait Monoid[T] {
def zero: T
def append(a: T, b: T): T
}
object Monoid extends ProductTypeClassCompanion[Monoid] {
def mzero[T](implicit mt: Monoid[T]) = mt.zero
implicit def booleanMonoid: Monoid[Boolean] = new Monoid[Boolean] {
def zero = false
def append(a: Boolean, b: Boolean) = a || b
}
implicit def intMonoid: Monoid[Int] = new Monoid[Int] {
def zero = 0
def append(a: Int, b: Int) = a + b
}
implicit def doubleMonoid: Monoid[Double] = new Monoid[Double] {
def zero = 0.0
def append(a: Double, b: Double) = a + b
}
implicit def stringMonoid: Monoid[String] = new Monoid[String] {
def zero = ""
def append(a: String, b: String) = a + b
}
object typeClass extends ProductTypeClass[Monoid] {
def emptyProduct = new Monoid[HNil] {
def zero = HNil
def append(a: HNil, b: HNil) = HNil
}
def product[F, T <: HList](mh: Monoid[F], mt: Monoid[T]) = new Monoid[F :: T] {
def zero = mh.zero :: mt.zero
def append(a: F :: T, b: F :: T) = mh.append(a.head, b.head) :: mt.append(a.tail, b.tail)
}
def project[F, G](instance: => Monoid[G], to: F => G, from: G => F) = new Monoid[F] {
def zero = from(instance.zero)
def append(a: F, b: F) = from(instance.append(to(a), to(b)))
}
}
}
trait MonoidSyntax[T] {
def |+|(b: T): T
}
object MonoidSyntax {
implicit def monoidSyntax[T](a: T)(implicit mt: Monoid[T]): MonoidSyntax[T] = new MonoidSyntax[T] {
def |+|(b: T) = mt.append(a, b)
}
}
//использование в лоб
case class Foo(i: Int, s: String)
val fc = Foo(13, "foo") |+| Foo(23, "bar")
/* как видите, назначение auto typeclass derivation несколько иное и требует
описания действий НАД объектами всех используемых типов, тогда как case one
предназначен только для генерации объектов типов, что в случае создания обертки
(конкретно этой или похожих на нее, а не любых других абстрактных оберток
над всеми библиотеками во Вселенной)
Безусловно, текущую реализацию case one можно смержить с case two,
добавив реализацию на макросах для генерации внутренних методов вида
*/
implicit def [typeName]Monoid: Monoid[T] = new Monoid[T] {
def zero = ???
def append(a: T, b: T) = ???
}
/*
в таком случае получится интересная реализация.
Важно помнить, что внутри shapeless происходит генерация
кода для вопсроизведения поведения, описанного в методах
для наследников ProductTypeClassCompanion и ProductTypeClass.
По сути, в case one вы сами прописываете как именно будет создаваться
тот или иной инстанс, а в case two вы описываете как производить операции над инстансами,
прописывая попутно и само создание. Для case one это лишняя работа
*/
//Вопрос: Там еще были бы и рекорды из коробки
/*
Если я правильно поняла, то вопрос в наименовании модели? В статье Record это тип,
возвращаемый драйвером.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment