Skip to content

Commit

Permalink
Merge pull request #79 from TinkoffCreditSystems/higher-kind
Browse files Browse the repository at this point in the history
Add more representableK instances, widening for logs, syntax for embe…
  • Loading branch information
Odomontois authored Dec 19, 2019
2 parents acef573 + 2b5d7fe commit fe7c7e0
Show file tree
Hide file tree
Showing 25 changed files with 122 additions and 60 deletions.
23 changes: 15 additions & 8 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Publish._, Dependencies._
import com.typesafe.sbt.SbtGit.git

val libVersion = "0.5.6"
val libVersion = "0.5.7"

lazy val setMinorVersion = minorVersion := {
CrossVersion.partialVersion(scalaVersion.value) match {
Expand Down Expand Up @@ -42,7 +42,14 @@ lazy val defaultSettings = Seq(

moduleName := "tofu"

lazy val core = project dependsOn opticsCore settings (
lazy val higherKindCore = project settings (
defaultSettings,
publishName := "core-higher-kind",
libraryDependencies ++= Seq(catsCore, catsTagless),
macros,
)

lazy val core = project dependsOn (opticsCore, higherKindCore) settings (
defaultSettings,
publishName := "core",
libraryDependencies ++= Seq(catsCore, catsEffect, catsTagless),
Expand Down Expand Up @@ -177,13 +184,13 @@ lazy val derivation =
lazy val zioCore =
project
.in(file("zio/core"))
.settings(defaultSettings, libraryDependencies ++= List(zio, zioCats))
.settings(defaultSettings, libraryDependencies ++= List(zio, zioCats), publishName := "zio-core")
.dependsOn(core, concurrent)

lazy val zioLogging =
project
.in(file("zio/logging"))
.settings(defaultSettings, libraryDependencies ++= List(zio, slf4j))
.settings(defaultSettings, libraryDependencies ++= List(zio, slf4j), publishName := "zio-logging")
.dependsOn(loggingStr)

lazy val zioInterop = project
Expand Down Expand Up @@ -279,10 +286,10 @@ lazy val defaultScalacOptions = scalacOptions ++= Seq(
"-unchecked", // Enable additional warnings where generated code depends on assumptions.

// Inlining options. More at https://www.lightbend.com/blog/scala-inliner-optimizer, https://github.com/scala/scala/pull/4858, https://github.com/scala/bug/issues/8790
"-opt:l:method", // Enable intra-method optimizations: unreachable-code,simplify-jumps,compact-locals,copy-propagation,redundant-casts,box-unbox,nullness-tracking,closure-invocations,allow-skip-core-module-init,assume-modules-non-null,allow-skip-class-loading.
"-opt:l:inline", // Enable cross-method optimizations (note: inlining requires -opt-inline-from): l:method,inline.
"-opt-inline-from:tofu.**", // Patterns for classfile names from which to allow inlining
"-opt-warnings:none", // No optimizer warnings.
"-opt:l:method", // Enable intra-method optimizations: unreachable-code,simplify-jumps,compact-locals,copy-propagation,redundant-casts,box-unbox,nullness-tracking,closure-invocations,allow-skip-core-module-init,assume-modules-non-null,allow-skip-class-loading.
"-opt:l:inline", // Enable cross-method optimizations (note: inlining requires -opt-inline-from): l:method,inline.
"-opt-inline-from:tofu.**", // Patterns for classfile names from which to allow inlining
"-opt-warnings:none", // No optimizer warnings.

// "-Xcheckinit", // Wrap field accessors to throw an exception on uninitialized access. (SHOULD BE USED ONLY IN DEV)
"-Xlint:adapted-args", // Warn if an argument list is modified to match the receiver.
Expand Down
11 changes: 7 additions & 4 deletions concurrent/src/main/scala/tofu/concurrent/Daemon.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ import cats.effect._
import cats.effect.concurrent.{MVar, TryableDeferred}
import cats.effect.syntax.bracket._
import cats.syntax.applicativeError._
import cats.tagless.autoApplyK
import cats.{Applicative, Apply, FlatMap, Monad}
import tofu.control.ApplicativeZip
import tofu.higherKind.Function2K
import tofu.higherKind.{Function2K, RepresentableK}
import tofu.syntax.monadic._
import tofu.syntax.start._

Expand Down Expand Up @@ -57,7 +56,6 @@ trait DaemonicInstances { self: Daemonic.type =>
)
}

@autoApplyK
trait Daemon[F[_], E, A] extends Fiber[F, A] {
def join: F[A]
def cancel: F[Unit]
Expand Down Expand Up @@ -183,7 +181,12 @@ final class TofuCanceledJoinException[F[_], A] private[tofu] (val daemon: Daemon
extends InterruptedException("trying to join canceled fiber")

trait DaemonInstances {
implicit def daemonApplicative[F[_], E](implicit F: Monad[F]): Applicative[Daemon[F, E, *]] =
private[this] val representableAny = higherKind.derived.genRepresentableK[Daemon[*[_], Any, Any]]

final implicit def daemonRepresentable[E, A]: RepresentableK[Daemon[*[_], E, A]] =
representableAny.asInstanceOf[RepresentableK[Daemon[*[_], E, A]]]

final implicit def daemonApplicative[F[_], E](implicit F: Monad[F]): Applicative[Daemon[F, E, *]] =
new ApplicativeZip[Daemon[F, E, *]] {
def pure[A](x: A): Daemon[F, E, A] = new Daemon[F, E, A] {
def join: F[A] = F.pure(x)
Expand Down
5 changes: 3 additions & 2 deletions core/src/main/scala/tofu/common/Console.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package tofu.common
import cats.effect.Sync
import cats.tagless.ApplyK
import tofu.higherKind
import tofu.higherKind.RepresentableK

import scala.io.StdIn

Expand All @@ -16,7 +17,7 @@ object Console {
def putStrLn[F[_]](s: String)(implicit C: Console[F]): F[Unit] = C.putStrLn(s)
def readStrLn[F[_]](implicit C: Console[F]): F[String] = C.readStrLn

implicit val applyKInstance: ApplyK[Console] = cats.tagless.Derive.applyK
implicit val representableKInstance: RepresentableK[Console] = higherKind.derived.genRepresentableK[Console]

implicit def syncInstance[F[_]](implicit F: Sync[F]): Console[F] = new Console[F] {
def putStr(s: String): F[Unit] = F.delay(print(s))
Expand Down
9 changes: 5 additions & 4 deletions core/src/main/scala/tofu/generate/GenRandom.scala
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package tofu.generate

import cats.effect.Sync

import scala.util.Random
import cats.syntax.functor._
import cats.tagless.ApplyK
import simulacrum.typeclass
import tofu.higherKind
import tofu.higherKind.RepresentableK

import scala.util.Random

@typeclass
trait GenRandom[F[_]] {
Expand Down Expand Up @@ -41,5 +42,5 @@ object GenRandom {
def nextInt(max: Int): F[Int] = F.delay(rnd.nextInt(max))
}

implicit val applyK: ApplyK[GenRandom] = cats.tagless.Derive.applyK
implicit val genRandomRepresentableK: RepresentableK[GenRandom] = higherKind.derived.genRepresentableK[GenRandom]
}
7 changes: 4 additions & 3 deletions core/src/main/scala/tofu/generate/GenUUID.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import java.util.UUID

import cats.Functor
import cats.effect.Sync
import simulacrum.typeclass
import cats.syntax.functor._
import cats.tagless.ApplyK
import simulacrum.typeclass
import tofu.higherKind
import tofu.higherKind.RepresentableK

@typeclass
trait GenUUID[F[_]] {
Expand All @@ -21,5 +22,5 @@ object GenUUID {
val randomUUID: F[UUID] = F.delay(UUID.randomUUID())
}

implicit val applyK: ApplyK[GenUUID] = cats.tagless.Derive.applyK
implicit val genUUIDRepresentableK: RepresentableK[GenUUID] = higherKind.derived.genRepresentableK[GenUUID]
}
25 changes: 0 additions & 25 deletions core/src/main/scala/tofu/higherKind/Function2K.scala

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ import simulacrum.typeclass
@typeclass trait Embed[U[_[_]]] {
def embed[F[_]: FlatMap](ft: F[U[F]]): U[F]
}

object Embed {
def of[U[_[_]], F[_]: FlatMap](fuf: F[U[F]])(implicit embed: Embed[U]): U[F] = embed.embed(fuf)
}
58 changes: 58 additions & 0 deletions higherKindCore/src/main/scala/tofu/higherKind/Function2K.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package tofu.higherKind
import cats.data.Tuple2K
import cats.{FlatMap, ~>}
import tofu.syntax.functionK.funK
import tofu.syntax.monadic._

trait Function2K[F[_], G[_], H[_]] {
def apply[A](fa: F[A], ga: G[A]): H[A]

def tupled: Tuple2K[F, G, *] ~> H = funK(t2k => apply(t2k.first, t2k.second))
}

object Function2K {
private[this] val representableAny = new Function2KRepresentable[Any, Any]

implicit def representableK[F[_], G[_]]: RepresentableK[Function2K[F, G, *[_]]] =
representableAny.asInstanceOf[RepresentableK[Function2K[F, G, *[_]]]]

def apply[F[_], G[_], H[_]](maker: MakeFunctionK[F, G, H]): Function2K[F, G, H] = maker

def untupled[F[_], G[_], H[_]](fk: Tuple2K[F, G, *] ~> H): Function2K[F, G, H] = apply((f, g) => fk(Tuple2K(f, g)))

abstract class MakeFunctionK[F[_], G[_], H[_]] extends Function2K[F, G, H] {

def applyArbitrary(f: F[Arbitrary], g: G[Arbitrary]): H[Arbitrary]

def apply[A](fa: F[A], ga: G[A]): H[A] =
applyArbitrary(fa.asInstanceOf[F[Arbitrary]], ga.asInstanceOf[G[Arbitrary]]).asInstanceOf[H[A]]
}
type Arbitrary

private class Function2KRepresentable[X[_], Y[_]] extends RepresentableK[Function2K[X, Y, *[_]]] {
final def tabulate[F[_]](hom: RepK[Function2K[X, Y, *[_]], *] ~> F): Function2K[X, Y, F] =
Function2K((xa, ya) => hom(RepK[Function2K[X, Y, *[_]]](_.apply(xa, ya))))

//just optimized allocationwise redefinitions
final override def mapK[F[_], G[_]](af: Function2K[X, Y, F])(fk: F ~> G): Function2K[X, Y, G] =
Function2K((xa, ya) => fk(af(xa, ya)))

final override def productK[F[_], G[_]](
af: Function2K[X, Y, F],
ag: Function2K[X, Y, G]
): Function2K[X, Y, Tuple2K[F, G, *]] =
Function2K((xa, ya) => Tuple2K(af(xa, ya), ag(xa, ya)))

final override def embed[F[_]: FlatMap](ft: F[Function2K[X, Y, F]]): Function2K[X, Y, F] =
Function2K((xa, ya) => ft.flatMap(_(xa, ya)))

override def zipWith2K[F[_], G[_], H[_]](af: Function2K[X, Y, F], ag: Function2K[X, Y, G])(
f2: Function2K[F, G, H]
): Function2K[X, Y, H] =
Function2K((xa, ya) => f2(af(xa, ya), ag(xa, ya)))

override def pureK[F[_]](p: Point[F]): Function2K[X, Y, F] =
Function2K((_, _) => p.point)
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package tofu.higherKind
import cats.{ContravariantMonoidal, MonoidK}
import cats.{ContravariantMonoidal, MonoidK, ~>}

/**
* equivalent of UnitK ~> F
Expand Down Expand Up @@ -31,4 +31,10 @@ object Point {
def contraMonoidal[F[_]](implicit F: ContravariantMonoidal[F]) = new Point[F] {
override def point[A]: F[A] = F.trivial
}

implicit val pointRepresentable: RepresentableK[Point] = new RepresentableK[Point] {
def tabulate[F[_]](hom: RepK[Point, *] ~> F): Point[F] = new Point[F] {
def point[A]: F[A] = hom(RepK[Point](_.point))
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package tofu
package higherKind
import cats.tagless.ApplyK
import cats.{Applicative, Apply, FlatMap, Monoid, MonoidK, Semigroup, SemigroupK, ~>}
import cats.{Applicative, Apply, Monoid, MonoidK, Semigroup, SemigroupK, ~>}
import tofu.syntax.functionK.funK
import tofu.syntax.monadic._

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,6 @@ class HigherKindedMacros(override val c: blackbox.Context) extends cats.tagless.

case method if method.occursInReturn(ff) =>
val params = method.paramLists.map(_.map(_.name))
val tresult = method.returnType match {
case TypeRef(__, _, List(param)) => param
case _ => abort(method.returnType.toString)
}
val body = q"$hom($repk[$algebra](($algv => $alg.${method.name}(...$params))))"
val tpe = appliedType(f, method.returnType.typeArgs)
method.copy(body = body, returnType = tpe)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
package tofu.syntax

import cats.~>

object functionK {
Expand Down
12 changes: 7 additions & 5 deletions logging/structured/src/main/scala/tofu/logging/Logging.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ package tofu.logging
import Logging._
import cats.kernel.Monoid
import cats.syntax.apply._
import cats.tagless.{ApplyK, Derive}
import cats.{Applicative, Apply, FlatMap}
import com.github.ghik.silencer.silent
import org.slf4j.{Logger, LoggerFactory, Marker}
import tofu.higherKind.Embed
import tofu.higherKind
import tofu.higherKind.{Embed, RepresentableK}
import tofu.logging.impl.EmbedLogging
import tofu.syntax.functionK._

Expand Down Expand Up @@ -69,7 +69,9 @@ trait ServiceLogging[F[_], +Service] extends LoggingBase[F]

/** typeclass for logging using specified logger or set of loggers
* see `Logs` for creating instances of that*/
trait Logging[F[_]] extends ServiceLogging[F, Nothing]
trait Logging[F[_]] extends ServiceLogging[F, Nothing] {
final def widen[G[a] >: F[a]]: Logging[G] = this.asInstanceOf[Logging[G]]
}

object Logging {
def apply[F[_]](implicit logging: Logging[F]): Logging[F] = logging
Expand All @@ -79,14 +81,14 @@ object Logging {

/** having two logging implementation call `first` after `second` */
def combine[F[_]: Apply](first: Logging[F], second: Logging[F]): Logging[F] =
loggingApplyK.map2K(first, second)(makeFunctionK(t => t.first *> t.second))
loggingRepresentable.map2K(first, second)(makeFunctionK(t => t.first *> t.second))

private[logging] def loggerForService[S](implicit ct: ClassTag[S]): Logger =
LoggerFactory.getLogger(ct.runtimeClass)

def flatten[F[_]: FlatMap](underlying: F[Logging[F]]): Logging[F] = new EmbedLogging[F](underlying)

implicit val loggingApplyK: ApplyK[Logging] = Derive.applyK[Logging]
implicit val loggingRepresentable: RepresentableK[Logging] = higherKind.derived.genRepresentableK[Logging]

implicit def loggingMonoid[F[_]: Applicative]: Monoid[Logging[F]] = new Monoid[Logging[F]] {
val empty: Logging[F] = Logging.empty[F]
Expand Down
10 changes: 10 additions & 0 deletions logging/structured/src/main/scala/tofu/logging/Logs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,28 @@ import cats.kernel.Monoid
import cats.{Applicative, Apply, FlatMap, Functor}
import impl.{ContextSyncLoggingImpl, SyncLogging}
import org.slf4j.LoggerFactory
import tofu.higherKind
import tofu.higherKind.RepresentableK
import tofu.syntax.monadic._

import scala.reflect.ClassTag

trait Logs[I[_], F[_]] {
def forService[Svc: ClassTag]: I[Logging[F]]
def byName(name: String): I[Logging[F]]

final def biwiden[I1[a] >: I[a], F1[a] >: F[a]]: Logs[I1, F1] = this.asInstanceOf[Logs[I1, F1]]
}

object Logs {
def apply[I[_], F[_]](implicit logs: Logs[I, F]): Logs[I, F] = logs

private [this] val logsRepresentableAny: RepresentableK[Logs[*[_], Any]] =
higherKind.derived.genRepresentableK[Logs[*[_], Any]]

implicit def logsRepresentable[Y[_]]: RepresentableK[Logs[*[_], Y]] =
logsRepresentableAny.asInstanceOf[RepresentableK[Logs[*[_], Y]]]

def provide[I[_], F[_]] = new Provide[I, F]
def provideM[I[_], F[_]] = new ProvideM[I, F]

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package tofu.optics.macros

import org.scalatest.{FunSuite, Matchers}
import tofu.optics.{Contains, Label, PContains}

class GenContainsSpec extends FunSuite with Matchers {

Expand Down
2 changes: 1 addition & 1 deletion zio/logging/src/main/scala/tofu/logging/ZLogs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import scala.reflect.ClassTag
trait ZLogs[R] extends Logs[UIO, URIO[R, *]]

object ZLogs {
val urio: Logs[UIO, UIO] = new Logs[UIO, UIO] {
val uio: Logs[UIO, UIO] = new Logs[UIO, UIO] {
def forService[Svc: ClassTag]: UIO[Logging[UIO]] =
UIO.effectTotal(new UIOZLogging(loggerForService[Svc]))
def byName(name: String): UIO[Logging[UIO]] = UIO.effectTotal(new UIOZLogging(LoggerFactory.getLogger(name)))
Expand Down

0 comments on commit fe7c7e0

Please sign in to comment.