Last active
October 15, 2019 11:24
-
-
Save DanyMariaLee/2774f10e64e6aaff2e855061aa74a16c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Вопрос: А разве нельзя было то же самое на 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