From c5afb1b1c8a5c246016ae42d0a5ba39e5a9e410e Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Thu, 26 Jan 2023 19:33:11 +0000 Subject: [PATCH 1/6] `EventProp` is a `Functor` --- calico/src/main/scala/calico/html/Prop.scala | 13 +++++++++++-- .../scala/calico/html/codegen/CalicoGenerator.scala | 2 +- .../calico/html/codegen/DomDefsGenerator.scala | 7 +++++-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/calico/src/main/scala/calico/html/Prop.scala b/calico/src/main/scala/calico/html/Prop.scala index a1988c8c..76743036 100644 --- a/calico/src/main/scala/calico/html/Prop.scala +++ b/calico/src/main/scala/calico/html/Prop.scala @@ -18,6 +18,7 @@ package calico package html import calico.syntax.* +import cats.Functor import cats.effect.kernel.Async import cats.effect.kernel.Resource import cats.syntax.all.* @@ -133,15 +134,23 @@ private trait PropModifiers[F[_]](using F: Async[F]): Modifier.forSignalResource[F, Any, OptionSignalResourceModifier[F, Any, Any], Option[Any]]( _.values) { (m, n) => setPropOption(n, m.name, m.codec) } -final class EventProp[F[_], E] private[calico] (key: String): +sealed abstract class EventProp[F[_], E, A] private[calico]: import EventProp.* - inline def -->(sink: Pipe[F, E, Nothing]): PipeModifier[F, E] = PipeModifier(key, sink) + def -->(sink: Pipe[F, A, Nothing]): PipeModifier[F, E] object EventProp: + def apply[F[_], E](key: String): EventProp[F, E, E] = new: + def -->(sink: Pipe[F, E, Nothing]) = PipeModifier(key, sink) + final class PipeModifier[F[_], E]( private[calico] val key: String, private[calico] val sink: Pipe[F, E, Nothing]) + given [F[_], E]: Functor[EventProp[F, E, _]] = new: + def map[A, B](fa: EventProp[F, E, A])(f: A => B): EventProp[F, E, B] = new: + def -->(sink: Pipe[F, B, Nothing]) = + fa --> (_.map(f).through(sink)) + private trait EventPropModifiers[F[_]](using F: Async[F]): import EventProp.* inline given forPipeEventProp[T <: fs2.dom.Node[F], E]: Modifier[F, T, PipeModifier[F, E]] = diff --git a/project/src/main/scala/calico/html/codegen/CalicoGenerator.scala b/project/src/main/scala/calico/html/codegen/CalicoGenerator.scala index f9e0a6af..cbbe2ae3 100644 --- a/project/src/main/scala/calico/html/codegen/CalicoGenerator.scala +++ b/project/src/main/scala/calico/html/codegen/CalicoGenerator.scala @@ -248,7 +248,7 @@ private[codegen] class CalicoGenerator(srcManaged: File) val baseImplDef = if (outputBaseImpl) List( - s"@inline private[calico] def ${keyImplName}[Ev <: ${baseScalaJsEventType}](key: String): ${keyKind}[F, Ev] = ${keyKindConstructor(keyKind)}(key)" + s"@inline private[calico] def ${keyImplName}[Ev <: ${baseScalaJsEventType}](key: String): ${keyKind}[F, Ev, Ev] = ${keyKind}(key)" ) else { Nil diff --git a/project/src/main/scala/calico/html/codegen/DomDefsGenerator.scala b/project/src/main/scala/calico/html/codegen/DomDefsGenerator.scala index fedb107d..56d28c92 100644 --- a/project/src/main/scala/calico/html/codegen/DomDefsGenerator.scala +++ b/project/src/main/scala/calico/html/codegen/DomDefsGenerator.scala @@ -278,7 +278,9 @@ object DomDefsGenerator { case (key, vals) => ( key, - vals.map(attr => attr.copy(scalaJsEventType = "F, " + attr.scalaJsEventType))) + vals.map(attr => + attr.copy(scalaJsEventType = + s"F, ${attr.scalaJsEventType}, ${attr.scalaJsEventType}"))) }, printDefGroupComments = true, traitCommentLines = Nil, @@ -311,7 +313,8 @@ object DomDefsGenerator { ( key, vals.map(attr => - attr.copy(scalaJsEventType = "F, " + attr.scalaJsEventType))) + attr.copy(scalaJsEventType = + s"F, ${attr.scalaJsEventType}, ${attr.scalaJsEventType}"))) }, printDefGroupComments = true, traitCommentLines = List(eventPropsDefGroups.head._1), From e1585f8a23ed48ce2a84ce5dabd79710c0f1d7a4 Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Thu, 26 Jan 2023 19:42:43 +0000 Subject: [PATCH 2/6] And also `FunctorFilter` --- calico/src/main/scala/calico/html/Prop.scala | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/calico/src/main/scala/calico/html/Prop.scala b/calico/src/main/scala/calico/html/Prop.scala index 76743036..b1f9adf6 100644 --- a/calico/src/main/scala/calico/html/Prop.scala +++ b/calico/src/main/scala/calico/html/Prop.scala @@ -19,6 +19,7 @@ package html import calico.syntax.* import cats.Functor +import cats.FunctorFilter import cats.effect.kernel.Async import cats.effect.kernel.Resource import cats.syntax.all.* @@ -151,6 +152,12 @@ object EventProp: def -->(sink: Pipe[F, B, Nothing]) = fa --> (_.map(f).through(sink)) + given [F[_], E]: FunctorFilter[EventProp[F, E, _]] = new: + def functor = summon + def mapFilter[A, B](fa: EventProp[F, E, A])(f: A => Option[B]): EventProp[F, E, B] = new: + def -->(sink: Pipe[F, B, Nothing]) = + fa --> (_.mapFilter(f).through(sink)) + private trait EventPropModifiers[F[_]](using F: Async[F]): import EventProp.* inline given forPipeEventProp[T <: fs2.dom.Node[F], E]: Modifier[F, T, PipeModifier[F, E]] = From 76f660200bdfd75acca4a3c940adbeac88306fd4 Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Thu, 26 Jan 2023 19:52:41 +0000 Subject: [PATCH 3/6] Minimize allocations --- calico/src/main/scala/calico/html/Prop.scala | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/calico/src/main/scala/calico/html/Prop.scala b/calico/src/main/scala/calico/html/Prop.scala index b1f9adf6..59c9d3c4 100644 --- a/calico/src/main/scala/calico/html/Prop.scala +++ b/calico/src/main/scala/calico/html/Prop.scala @@ -20,6 +20,7 @@ package html import calico.syntax.* import cats.Functor import cats.FunctorFilter +import cats.Id import cats.effect.kernel.Async import cats.effect.kernel.Resource import cats.syntax.all.* @@ -147,15 +148,19 @@ object EventProp: private[calico] val key: String, private[calico] val sink: Pipe[F, E, Nothing]) - given [F[_], E]: Functor[EventProp[F, E, _]] = new: - def map[A, B](fa: EventProp[F, E, A])(f: A => B): EventProp[F, E, B] = new: - def -->(sink: Pipe[F, B, Nothing]) = + inline given [F[_], E]: Functor[EventProp[F, E, _]] = + _functor.asInstanceOf[Functor[EventProp[F, E, _]]] + private val _functor: Functor[EventProp[Id, Any, _]] = new: + def map[A, B](fa: EventProp[Id, Any, A])(f: A => B) = new: + def -->(sink: Pipe[Id, B, Nothing]) = fa --> (_.map(f).through(sink)) - given [F[_], E]: FunctorFilter[EventProp[F, E, _]] = new: - def functor = summon - def mapFilter[A, B](fa: EventProp[F, E, A])(f: A => Option[B]): EventProp[F, E, B] = new: - def -->(sink: Pipe[F, B, Nothing]) = + inline given [F[_], E]: FunctorFilter[EventProp[F, E, _]] = + _functorFilter.asInstanceOf[FunctorFilter[EventProp[F, E, _]]] + private val _functorFilter: FunctorFilter[EventProp[Id, Any, _]] = new: + def functor = _functor + def mapFilter[A, B](fa: EventProp[Id, Any, A])(f: A => Option[B]) = new: + def -->(sink: Pipe[Id, B, Nothing]) = fa --> (_.mapFilter(f).through(sink)) private trait EventPropModifiers[F[_]](using F: Async[F]): From 033738a4496b975f50190602ef518ad6c055ca15 Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Thu, 26 Jan 2023 20:00:50 +0000 Subject: [PATCH 4/6] Aggregate instances --- calico/src/main/scala/calico/html/Prop.scala | 25 +++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/calico/src/main/scala/calico/html/Prop.scala b/calico/src/main/scala/calico/html/Prop.scala index 59c9d3c4..df92799b 100644 --- a/calico/src/main/scala/calico/html/Prop.scala +++ b/calico/src/main/scala/calico/html/Prop.scala @@ -148,20 +148,17 @@ object EventProp: private[calico] val key: String, private[calico] val sink: Pipe[F, E, Nothing]) - inline given [F[_], E]: Functor[EventProp[F, E, _]] = - _functor.asInstanceOf[Functor[EventProp[F, E, _]]] - private val _functor: Functor[EventProp[Id, Any, _]] = new: - def map[A, B](fa: EventProp[Id, Any, A])(f: A => B) = new: - def -->(sink: Pipe[Id, B, Nothing]) = - fa --> (_.map(f).through(sink)) - - inline given [F[_], E]: FunctorFilter[EventProp[F, E, _]] = - _functorFilter.asInstanceOf[FunctorFilter[EventProp[F, E, _]]] - private val _functorFilter: FunctorFilter[EventProp[Id, Any, _]] = new: - def functor = _functor - def mapFilter[A, B](fa: EventProp[Id, Any, A])(f: A => Option[B]) = new: - def -->(sink: Pipe[Id, B, Nothing]) = - fa --> (_.mapFilter(f).through(sink)) + inline given [F[_], E]: (Functor[EventProp[F, E, _]] & FunctorFilter[EventProp[F, E, _]]) = + _functor.asInstanceOf[Functor[EventProp[F, E, _]] & FunctorFilter[EventProp[F, E, _]]] + private val _functor: Functor[EventProp[Id, Any, _]] & FunctorFilter[EventProp[Id, Any, _]] = + new Functor[EventProp[Id, Any, _]] with FunctorFilter[EventProp[Id, Any, _]]: + def map[A, B](fa: EventProp[Id, Any, A])(f: A => B) = new: + def -->(sink: Pipe[Id, B, Nothing]) = + fa --> (_.map(f).through(sink)) + def functor = this + def mapFilter[A, B](fa: EventProp[Id, Any, A])(f: A => Option[B]) = new: + def -->(sink: Pipe[Id, B, Nothing]) = + fa --> (_.mapFilter(f).through(sink)) private trait EventPropModifiers[F[_]](using F: Async[F]): import EventProp.* From 2b690ff3b540a331b8ee01672f3010cd3bbc4a7a Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Thu, 26 Jan 2023 23:46:17 +0000 Subject: [PATCH 5/6] Try a different encoding --- calico/src/main/scala/calico/html/Prop.scala | 22 +++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/calico/src/main/scala/calico/html/Prop.scala b/calico/src/main/scala/calico/html/Prop.scala index df92799b..d4e41496 100644 --- a/calico/src/main/scala/calico/html/Prop.scala +++ b/calico/src/main/scala/calico/html/Prop.scala @@ -136,13 +136,18 @@ private trait PropModifiers[F[_]](using F: Async[F]): Modifier.forSignalResource[F, Any, OptionSignalResourceModifier[F, Any, Any], Option[Any]]( _.values) { (m, n) => setPropOption(n, m.name, m.codec) } -sealed abstract class EventProp[F[_], E, A] private[calico]: +final class EventProp[F[_], E, A] private[calico] (key: String, pipe: Pipe[F, E, A]): import EventProp.* - def -->(sink: Pipe[F, A, Nothing]): PipeModifier[F, E] + + @inline def -->(sink: Pipe[F, A, Nothing]): PipeModifier[F, E] = + PipeModifier(key, pipe.andThen(sink)) + + private def through[B](pipe: Pipe[F, A, B]): EventProp[F, E, B] = + new EventProp(key, this.pipe.andThen(pipe)) object EventProp: - def apply[F[_], E](key: String): EventProp[F, E, E] = new: - def -->(sink: Pipe[F, E, Nothing]) = PipeModifier(key, sink) + def apply[F[_], E](key: String): EventProp[F, E, E] = + new EventProp(key, identity(_)) final class PipeModifier[F[_], E]( private[calico] val key: String, @@ -152,13 +157,10 @@ object EventProp: _functor.asInstanceOf[Functor[EventProp[F, E, _]] & FunctorFilter[EventProp[F, E, _]]] private val _functor: Functor[EventProp[Id, Any, _]] & FunctorFilter[EventProp[Id, Any, _]] = new Functor[EventProp[Id, Any, _]] with FunctorFilter[EventProp[Id, Any, _]]: - def map[A, B](fa: EventProp[Id, Any, A])(f: A => B) = new: - def -->(sink: Pipe[Id, B, Nothing]) = - fa --> (_.map(f).through(sink)) + def map[A, B](fa: EventProp[Id, Any, A])(f: A => B) = fa.through(_.map(f)) def functor = this - def mapFilter[A, B](fa: EventProp[Id, Any, A])(f: A => Option[B]) = new: - def -->(sink: Pipe[Id, B, Nothing]) = - fa --> (_.mapFilter(f).through(sink)) + def mapFilter[A, B](fa: EventProp[Id, Any, A])(f: A => Option[B]) = + fa.through(_.mapFilter(f)) private trait EventPropModifiers[F[_]](using F: Async[F]): import EventProp.* From 603483195b8fda9e8b51af612904b9bd6fa3e25d Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Thu, 26 Jan 2023 23:48:00 +0000 Subject: [PATCH 6/6] Fix visibility, inline --- calico/src/main/scala/calico/html/Prop.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/calico/src/main/scala/calico/html/Prop.scala b/calico/src/main/scala/calico/html/Prop.scala index d4e41496..04f3bd50 100644 --- a/calico/src/main/scala/calico/html/Prop.scala +++ b/calico/src/main/scala/calico/html/Prop.scala @@ -142,14 +142,14 @@ final class EventProp[F[_], E, A] private[calico] (key: String, pipe: Pipe[F, E, @inline def -->(sink: Pipe[F, A, Nothing]): PipeModifier[F, E] = PipeModifier(key, pipe.andThen(sink)) - private def through[B](pipe: Pipe[F, A, B]): EventProp[F, E, B] = + @inline private def through[B](pipe: Pipe[F, A, B]): EventProp[F, E, B] = new EventProp(key, this.pipe.andThen(pipe)) object EventProp: def apply[F[_], E](key: String): EventProp[F, E, E] = new EventProp(key, identity(_)) - final class PipeModifier[F[_], E]( + final class PipeModifier[F[_], E] private[calico] ( private[calico] val key: String, private[calico] val sink: Pipe[F, E, Nothing])