typelevel / cats-tagless Goto Github PK
View Code? Open in Web Editor NEWLibrary of utilities for tagless final encoded algebras
Home Page: https://typelevel.org/cats-tagless/
License: Apache License 2.0
Library of utilities for tagless final encoded algebras
Home Page: https://typelevel.org/cats-tagless/
License: Apache License 2.0
@autoInvariantK
fails on this code
@autoInvariantK
trait InvariantFail[F[_]] {
def function(f: String => F[Boolean]): F[Boolean]
}
Error
[error] object creation impossible, since method function in trait InvariantFail of type (f: String => G[Boolean])G[Boolean] is not defined
[error] method function overrides nothing.
[error] Note: the super classes of <$anon: ore.auth.InvariantFail[G]> contain the following, non final members named function:
[error] def function(f: String => G[Boolean]): G[Boolean]
[error] object creation impossible, since method function in trait InvariantFail of type (f: String => G[Boolean])G[Boolean] is not defined
[error] method function overrides nothing.
[error] Note: the super classes of <$anon: ore.auth.InvariantFail[G]> contain the following, non final members named function:
[error] def function(f: String => G[Boolean]): G[Boolean]
[error] four errors found
I'd like to complete my wip autoApplyK
in mainecoon and then started to move.
It might not an issue, but I did every step exactly in the document, added compiler and dependenceis, copied your Increment example. And it failed to compile say flagMap is not a method of F[Int]. Please advice.
This migration involves three areas of logic, in priority high to low order. We can do these in separate PRs.
cats.tagless.Derive
. e.g. implicit val functorKForAlg: FunctorK[Alg] =
Derive.functorK[Alg]
This need to handle situation where Alg with extra type params other than the effect type
The actual derivation is provide by the cats.tagless.Derive
from the cats-tagless-macro module, the annotation just need to generate delegate methods (main challenge is to deal with different shapes of Alg types).
derivation instances for algebra with abstract type members. We need normal instances and instances that are fully refined under an object in the companion named fullyRefined
, see https://github.com/typelevel/cats-tagless/blob/master/legacy-macros/src/main/scala/cats/tagless/autoFunctorK.scala#L72
for existing scala meta implementation. again the actual derivation is supported by the cats.tagless.Derive
from the cats-tagless-macro module
optional, alg instance summoning method like def apply[F[_]](implicit ev: Alg[F]): Alg[F]
I am not sure if that's a bloop issue or cats-tagless issue, but when I try compilation with bloop compiler, looks like macro annotations are not being expanded.
If I try doing the same with sbt, everything compiles fine.
I've been trying hard to enable more debugging output during macro generation but failed, any hits are appreciated.
I've assembled simplest reproducible case in this repo: https://github.com/ytaras/tagless_failure_showcase
2.13 is weeks away, we should try to get cats-tagless to build with it already :)
The following does not work:
trait DepFn[A] {
type Out
}
@autoFunctorK
trait AlgWithDependentType[F[_]] {
def dependent[A](f: DepFn[A])(out: f.Out): F[A => f.Out]
}
Unfortunately couldn't find a way to make it work.
Adding an @autoProfunctor
annotation would require reworking AlgDefn
a bit to handle a list of type parameters instead of only one.
I want to drop a support of Liberator in favour of cats-tagless
. But there are two very useful types that I need. And I think they may be a good fit for cats-tagless
.
Invocation[M[_[_]], A]
trait Invocation[M[_[_]], A] {
def invoke[F[_]](target: M[F]): F[A]
}
allows to encode some call of M[F]
resulting in F[A]
it's like a dual of Free algebra base functor.
ReifiedInvocations[M[_[_]]]
type classtrait ReifiedInvocations[M[_[_]]] extends FunctorK[M] { // This inheritance is arguable
def invocations: M[Invocation[M, ?]]
def mapInvocations[F[_]](f: Invocation[M, ?] ~> F): M[F] = mapK(invocations, f)
}
allows to create an instance of M[Invocation[M, ?]]
that is very useful for transformation of tagless algebras (see Aecor). There is also a macro for its derivation.
If you find this interesting I will send a PR.
P.S. One interesting intuition is that M[F]
is a collection of applied F[_]
indexed by Invocation[M, A]
:)
This is obviously a bug but also an enhancement because once it's fixed we could define final derived methods on algebras which would not be subject to the restrictions for deriving the typeclass.
Example:
case class ServerDependencies[F[_]](
foo: FooService[F],
bar: BarService[F],
other: NotTaglessThing
)
I'd expect it to generate something like:
def mapK[G[_]](f: F ~> G): ServerDependencies[G] =
ServerDependencies(foo.mapK(f), bar.mapK(f), other)
import cats.tagless.implicits._
import cats.tagless.finalAlg
import cats.tagless.autoFunctorK
import cats.arrow.FunctionK
//or autoFunctorK + autoInstrument, or finalAlg + autoInstrument, etc.
@autoFunctorK
@finalAlg
trait Foo[F[_]] {
def bar(a: Int): F[Int]
}
//object Foo {}
object Main extends App {
val foo: Foo[cats.Id] = _ => 42
println(foo.mapK(FunctionK.id))
}
This will result in java.lang.NoClassDefFoundError: Foo$
. Uncommenting the companion object fixes things.
The readme mentions to only use the compiler plugin on 2.12 - how should build.sbt be set up for a project that's cross-building 2.13 and 2.13?
It would be nice to have access to names and values of the parameters for the given call.
Why would this be useful?
This will likely be problematic because of the types, but maybe there's something we can do about this.
The basic idea: for an algebra like
trait Foo[F[_]] {
def bar(a: String, b: Int): F[Int]
}
I imagine we could have a type like
trait Parameter[Constraints[_]] {
type T
def constraints: Constraints[T]
def name: String
def value: T //by name?
}
included in Instrumentation
:
final case class Instrumentation[F[_], Constraints[_], A](
value: F[A],
algebraName: String,
methodName: String,
parameters: List[List[Parameter[Constraints]]],
)
and somehow define the constraints that we require each parameter to have, at the point of instrumenting:
val foo: Foo[IO] = ???
//Show[String] and Show[Int] would be required here
val instrumented: Foo[Instrumentation[IO, Show, *]] = foo.instrument[Show]
val result = instrumented.bar("baz", 42)
val stringified = result.parameters.head.map { param =>
param.name + ": " + param.constraints.show(param.value)
}.mkString(", ") //a: "baz", b: 42
I think kinds aren't a problem in this particular case, as parameters will need to be values, so *
-kinded things (exactly what Constraints[_]
expects).
For multiple implicits (which I don't think would be a common case, as Show should be enough for most usages), one could define a custom case class with derivations from/to it based on other implicits:
final case class Constraints[A](show: Show[A], monoid: Monoid[A])
implicit def constraintsToShow[A](implicit constraints: Constraints[A]): Show[A] = ...
or something like EList
(implicit evidence list), as seen in @non's talk: https://youtu.be/O78hnJuzQwA?t=1652
object foo {
import cats.implicits._
type Constraints[A] = Show[A] :/: Monoid[A] :/: ENil
val constraints: Constraints[String] = ... //some implicit derivation?
}
object bar {
import foo.constraints
Show[String] //derives from Constraints[String]
}
An example of one of my algebras:
trait Users[F[_]] {
def login(username: String Refined Email, password: String Refined Password): F[Option[ExchangeResult]]
def register(user: User, password: String Refined Password): F[Option[UserId]]
}
Each algebra is tested in isolation as most of them have side effects and therefore I test them against a real database.
Now in my service layer I use multiple algebras together to run a specific flow. These flows can fail and therefore a typical service method uses EitherT[F, ErrType, Res]
. As I've tested the algebras already I can just stub them. I find myself writing this a lot:
class DummyUsers[F[_]: Applicative] extends Users[F] {
def login(username: Refined[String, Email], password: Refined[String, Password]): F[Option[ExchangeResult]] =
Applicative[F].pure(None)
def register(user: User, password: Refined[String, Password]): F[Option[UserId]] =
Applicative[F].pure(None)
}
As you can see here I constraint F[_]
to be a Applicative
such that I can use Id
or Writer
to be flexible in that regard. When I want to alter behavior, I subclass the DummyUsers
class and I override the method.
This however generates a lot of boilerplate
Given I have defined a Default
type classes which returns a default value for a given type
trait Default[A] {
def value: A
}
I have instances defined for basic types. I would like to derive a Default
type class instance for my Users
Writing this explicitly would look something like this
class UsersDefault extends Users[Default] {
override def login(
username: Refined[String, Email],
password: Refined[String, Password]
): Default[Option[ExchangeResult]] = implicitly
override def register(
user: User,
password: Refined[String, Password]
): Default[Option[UserId]] = implicitly
}
As you can see everything is implicitly
. Could we use a scalamacro for that to generate such a class?
Ideally I would sub class this such that I can inherit and override certain methods.
Since we a Default
type class instance for Users
now I would like to lift the values inside the Applicative
. This is a matter of writing a natural transformation of Default ~> F
where F[_] : Applicative
. This is easy, but how would that work with sub classing? e.g. pseudo code:
object Dummy {
implicit def defaultToApplicative[F[_] : Applicative]: Default ~> F = ???
// this class would extend the `Users` algebra and use the `defaultToApplicative` to find a instance of `Users[Default]` which can be turned in to `Users[F]` by applying the transformation
@GenImpl(Users)
class DummyUsers[F[_] : Applicative]
}
I would rather avoid using scalameta, as it breaks with scoverage
This a rough sketch and a thought experiment to solve the problem. There might be other solutions as well, happy to hear your thoughts!
Intellij needs a special plugin to expand macro annotations.
See https://blog.jetbrains.com/scala/2015/10/14/intellij-api-to-build-scala-macros-support/
I couldn't find enough info about this so we might want to check how simulacrum has implemented it.
Currently (0.7) this fails to derive:
@autoFunctorK
trait Foo[F[_]] {
def bar(is: Int*): F[Int]
}
Since one major part of cats-tagless
are the macro annotations, the documentation should explain them clearly. At present, the documentation is a bit mixed in the main page.
For each macro annotation, a documentation page should be written containing the following:
FunctorK
in the companion object.object
, trait
, case class
, etc) it can be applied to.Motivation: I'm having to deal with Tapir, using it to generate client and service implementations for some APIs. I end up with values like these:
// Pure specification for an endpoint, used by the client and the server
val fooEndpoint: Endpoint[(AuthToken, FooInput), ApiError, FooOutput, Any]
// Complementary server-side implementation, without authentication.
val fooImpl: (AuthedClient, FooInput) => IO[Either[ApiError, FooOutput]]
// Endpoint + implementation
val fooServerEndpoint: ServerEndpoint[(AuthToken, FooInput), ApiError, FooOutput, Any, IO]
// Authentication, common for all endpoints
val authenticate: AuthToken => IO[Either[ApiError, AuthedClient]]
If you squint hard enough (and if you have enough endpoints to generalize), you can see that the three foo*
values belong in three variants of the same shape that describes the API as a whole:
trait Api[F[_, _]] {
def foo: F[FooInput, FooOutput]
// more endpoints here
}
type Endpoint0[A, B] = Endpoint[(AuthToken, A), ApiError, B, Any]
type PureApi = Api[Endpoint0]
type Impl[A, B] = (AuthedClient, A) => IO[Either[ApiError, B]]
type ImplApi = Api[Impl]
type ServerEndpoint0[F[_], A, B] = ServerEndpoint[(AuthToken, A), ApiError, B, Any, F]
type ServerApi[F[_]] = Api[ServerEndpoint0[F, *, *]]
Authentication could be factored out if only Api
had a version of mapK
dealing with bifunctors (bimapK
?). In the same vein, combining fooEndpoint
s and fooImpl
s is pure boilerplate, and could be implemented using something similar to map2K
(bimap2K
?).
Similar abstractions for trifunctors might also be useful, in case error types differ between endpoints.
Currently FunctorK provides mapK:
def mapK[F[_], G[_]](af: Alg[F])(fk: F ~> G): Alg[G]
I've had the use case where I'd like to do a mapK, but have the method name of the Alg (it was a trait) available. A simple example would be me wanting to log every method call on an Alg (using mapK to map to the same effect).
I think it'd be useful to have something like:
def namedMapK[F[_], G[_]](af: Alg[F])(methodName: String, fk: F ~> G): Alg[G]
If this is something you believe makes sense to have, I'd be happy to contribute. If you believe I'm looking at it the wrong way or there's a better approach, I'll also be happy to read your thoughts.
I have been trying out https://github.com/Dwolla/natchez-tagless/tree/main for tracing and been really interested in this idea of typeclass aop. Unfortunately I cannot see how an implementation of Aspect that works on tagless algebras can reason with any algebra that does not have F
as the outermost type constructor; A Stream[Instrumentation[F, *], *]
doesn't make much sense, and the aspect can't exactly change the return signature into Instrumentation[Stream[F, *], *]
.
However, what about just reasoning with type-costructors with typeclasses also?
Consider the following implementation of an aspect that allows any structure that embeds an F
, and as an example, tracing streams becomes trivial:
trait Aspect[A, Dom[_], Cod[_]] {
def weave(a: A)(ak: Aspect.Weave[Dom, Cod, *] ~> Id): A
}
object Aspect {
trait Advice[F[_], G[_]] {
type A
def name: String
def target: F[A]
implicit def instance: G[A]
}
object Advice {
type Aux[F[_], G[_], A0] = Advice[F, G] { type A = A0 }
def apply[F[_], G[_], A](
name: String,
target: F[A]
)(implicit instance: G[A]): Aux[F, G, A] = {
type A0 = A
val name0 = name
val target0 = target
val instance0 = instance
new Advice[F, G] {
type A = A0
val name = name0
val target = target0
val instance = instance0
}
}
}
case class Weave[Dom[_], Cod[_], A](
algebraName: String,
domain: List[List[Advice[Eval, Dom]]],
codomain: Advice.Aux[Id, Cod, A]
)
}
trait Effect[A] {
type F[_]
type U
def ev: F[U] =:= A
}
object Effect {
type Aux[F0[_], U0] = Effect[F0[U0]] {
type F[x] = F0[x]
type U = U0
}
}
implicit def effectForAnyKind[F[_], A]: Effect.Aux[F, A] = {
type F0[B] = F[B]
new Effect[F[A]] {
type F[C] = F0[C]
type U = A
val ev = implicitly
}
}
trait TraceableEffect[A] {
val effect: Effect[A]
def trace: Trace[effect.F]
def F: Applicative[effect.F]
}
object TraceableEffect {
type Aux[F0[_], A0] = TraceableEffect[F0[A0]] {
val effect: Effect.Aux[F0, A0]
}
}
implicit def traceableEffectForAnyTraceableEffect[F[_]: Trace, A](implicit
effect: Effect.Aux[F, A],
F0: Applicative[F]
): TraceableEffect.Aux[F, A] = {
val effect0 = effect
new TraceableEffect[F[A]] {
val effect = effect0
def trace = Trace[F]
def F = F0
}
}
def applyTracingAspect: Aspect.Weave[Trivial, TraceableEffect, *] ~> Id =
new (Aspect.Weave[Trivial, TraceableEffect, *] ~> Id) {
override def apply[A](fa: Aspect.Weave[Trivial, TraceableEffect, A]): Id[A] = {
val inst = fa.codomain.instance
val T: Trace[inst.effect.F] = inst.trace
val ev = inst.effect.ev
ev {
T.span(s"${fa.algebraName}.${fa.codomain.name}") {
ev.flip(fa.codomain.target)
}
}
}
}
trait MyAlg[F[_]] {
def getData(id: String): F[String]
def getDataStream(id: String): fs2.Stream[F, String]
}
def aspectForMyAlg[F[_]: Trace: MonadCancelThrow]: Aspect[MyAlg[F], Trivial, TraceableEffect] = {
def aspectConstructionMacro[Dom[_], Cod[_]](implicit
d1: Dom[String],
c1: Cod[F[String]],
c2: Cod[fs2.Stream[F, String]]
) = new Aspect[MyAlg[F], Dom, Cod] {
override def weave(a: MyAlg[F])(ak: Aspect.Weave[Dom, Cod, *] ~> Id): MyAlg[F] =
new MyAlg[F] {
override def getData(id: String): F[String] =
ak {
Aspect.Weave(
"MyAlg",
List(List(Aspect.Advice("id", Eval.now(id)))),
Aspect.Advice[Id, Cod, F[String]]("getData", a.getData(id))
)
}
override def getDataStream(id: String): fs2.Stream[F, String] =
ak {
Aspect.Weave(
"MyAlg",
List(List(Aspect.Advice("id", Eval.now(id)))),
Aspect.Advice[Id, Cod, fs2.Stream[F, String]]("getDataStream", a.getDataStream(id))
)
}
}
}
aspectConstructionMacro[Trivial, TraceableEffect]
}
def traceMyAlg[F[_]: Trace: MonadCancelThrow](alg: MyAlg[F]): MyAlg[F] =
aspectForMyAlg[F].weave(alg)(applyTracingAspect)
It would be very nice to use this with a semiauto encoding in the companion objects. I know circe and shapeless does something like this for Encoder/Decoder/Generic and I'm unsure if it could be leveraged with the existing macro definitions to create something like the below encoding
implicit val functorKAlg = deriveFunctorK[MyAlgebra]
When you use cats-tagless autoFunctorK on a algebra with scoverage the coverageReport / coverageAggregate bails out:
[error] java.lang.RuntimeException: No source root found for '/Users/markdejong/Projects/test/<macro>' (source roots: '/Users/markdejong/Projects/test/modules/domain/src/main/scala/')
[error] at scoverage.report.BaseReportWriter.relativeSource(BaseReportWriter.scala:28)
[error] at scoverage.report.BaseReportWriter.relativeSource(BaseReportWriter.scala:16)
[error] at scoverage.report.CoberturaXmlWriter.klass(CoberturaXmlWriter.scala:42)
[error] at scoverage.report.CoberturaXmlWriter.$anonfun$pack$1(CoberturaXmlWriter.scala:66)
[error] at scala.collection.TraversableLike.$anonfun$map$1(TraversableLike.scala:234)
[error] at scala.collection.immutable.List.foreach(List.scala:389)
[error] at scala.collection.TraversableLike.map(TraversableLike.scala:234)
[error] at scala.collection.TraversableLike.map$(TraversableLike.scala:227)
[error] at scala.collection.immutable.List.map(List.scala:295)
[error] at scoverage.report.CoberturaXmlWriter.pack(CoberturaXmlWriter.scala:66)
[error] at scoverage.report.CoberturaXmlWriter.$anonfun$xml$3(CoberturaXmlWriter.scala:90)
[error] at scala.collection.TraversableLike.$anonfun$map$1(TraversableLike.scala:234)
[error] at scala.collection.immutable.List.foreach(List.scala:389)
[error] at scala.collection.TraversableLike.map(TraversableLike.scala:234)
[error] at scala.collection.TraversableLike.map$(TraversableLike.scala:227)
[error] at scala.collection.immutable.List.map(List.scala:295)
[error] at scoverage.report.CoberturaXmlWriter.xml(CoberturaXmlWriter.scala:90)
[error] at scoverage.report.CoberturaXmlWriter.write(CoberturaXmlWriter.scala:20)
[error] at scoverage.ScoverageSbtPlugin$.writeReports(ScoverageSbtPlugin.scala:177)
[error] at scoverage.ScoverageSbtPlugin$.$anonfun$coverageReport0$1(ScoverageSbtPlugin.scala:124)
[error] at scoverage.ScoverageSbtPlugin$.$anonfun$coverageReport0$1$adapted(ScoverageSbtPlugin.scala:105)
[error] at scala.Function1.$anonfun$compose$1(Function1.scala:44)
[error] at sbt.internal.util.$tilde$greater.$anonfun$$u2219$1(TypeFunctions.scala:40)
[error] at sbt.std.Transform$$anon$4.work(System.scala:67)
[error] at sbt.Execute.$anonfun$submit$2(Execute.scala:269)
[error] at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:16)
[error] at sbt.Execute.work(Execute.scala:278)
[error] at sbt.Execute.$anonfun$submit$1(Execute.scala:269)
[error] at sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:178)
[error] at sbt.CompletionService$$anon$2.call(CompletionService.scala:37)
[error] at java.util.concurrent.FutureTask.run(FutureTask.java:266)
[error] at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
[error] at java.util.concurrent.FutureTask.run(FutureTask.java:266)
[error] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
[error] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
[error] at java.lang.Thread.run(Thread.java:748)
This makes sense to complete the picture in #56
FunctorK
and ContravariantK
are dual and would depend on each other for derivation.
Right now FunctorK
cannot handle Cokleisli
in parameter position.
Hi! I was searching for something that would resemble this:
def deferK[F[_]: FlatMap](fa: F[Alg[F]]): Alg[F]
but didn't find anything. Long story short, I played around and came up with this:
case class Suspended[Alg[_[_]], F[_], A](effect: Alg[F] => F[A])
trait SuspendK[Alg[_[_]]] {
def suspendK[F[_], G[_]](fk: Suspended[Alg, F, *] ~> G): Alg[G]
}
which is similar to aspects, but doesn't require an instance to call the attached method.
It turns out suspendK
is enough to implement mapK
as well as deferK
:
//> using scala "2.13.8"
//> using plugin "org.typelevel:::kind-projector:0.13.2"
//> using lib "org.typelevel::cats-core:2.8.0"
//> using lib "org.typelevel::cats-tagless-core:0.14.0"
import cats.FlatMap
import cats.~>
import cats.implicits._
import cats.Id
import cats.data.Nested
import cats.tagless.FunctorK
import cats.tagless.Trivial
case class Suspended[Alg[_[_]], F[_], A](effect: Alg[F] => F[A])
trait SuspendK[Alg[_[_]]] extends FunctorK[Alg] {
def suspendK[F[_], G[_]](fk: Suspended[Alg, F, *] ~> G): Alg[G]
def mapK[F[_], G[_]](af: Alg[F])(fk: F ~> G): Alg[G] = suspendK(
new (Suspended[Alg, F, *] ~> G) {
def apply[A](fa: Suspended[Alg, F, A]): G[A] = fk(fa.effect(af))
}
)
def deferK[F[_]: FlatMap, G[_]](fa: F[Alg[G]]): Alg[Nested[F, G, *]] =
suspendK {
new (Suspended[Alg, G, *] ~> Nested[F, G, *]) {
def apply[A](fk: Suspended[Alg, G, A]): Nested[F, G, A] =
fa.map(fk.effect(_)).nested
}
}
def deferKId[F[_]: FlatMap](fa: F[Alg[F]]): Alg[F] =
suspendK {
new (Suspended[Alg, F, *] ~> F) {
def apply[A](fk: Suspended[Alg, F, A]): F[A] =
fa.flatMap(fk.effect(_))
}
}
}
trait MyAlg[F[_]] {
def foo(s: String): F[Int]
def bar(i: Int): F[String]
}
object MyAlg {
implicit val suspendKMyAlg: SuspendK[MyAlg] =
new SuspendK[MyAlg] {
def suspendK[F[_], G[_]](fk: Suspended[MyAlg, F, *] ~> G): MyAlg[G] =
new MyAlg[G] {
def foo(s: String): G[Int] = fk.apply(Suspended(_.foo(s)))
def bar(i: Int): G[String] = fk.apply(Suspended(_.bar(i)))
}
}
}
Does this seem like a valuable addition? I certainly could use deferKId
, not sure how useful the others would be.
As for laws, so far I only have this one:
suspendK(
new (Suspended[Alg, F, *] ~> F) {
def apply[A](fa: Suspended[Alg, F, A]): F[A] = fa.effect(af)
}
) <-> af
Update: I guess this doesn't really have to be a generalization of FunctorK
, it could be redefined to have this operation:
def suspend[F[_]]: Alg[Suspended[Alg, F, *]]
then mapK
would be inherited from FunctorK
normally, and suspendK
would be derived like this:
def suspendK[F[_], G[_]](fk: Suspended[Alg, F, *] ~> G): Alg[G] =
mapK[Suspended[Alg, F, *], G](suspend)(fk)
i.e. documentation for the features originally from Sphynx. There is a blog post by @LukaJCB https://typelevel.org/blog/2017/12/27/optimizing-final-tagless.html about these features.
See SemigroupalK
and its derivation.
Compilation will fail, if the algebra has default arguments.
As an example, the following code will not compile
import cats._, cats.implicits._, cats.data._
import cats.tagless._, cats.tagless.implicits._
trait Alg[F[_]] {
def test(msg: String = "Hello World"): F[String]
}
object Alg {
implicit def functorK: FunctorK[Alg] = Derive.functorK[Alg]
}
class AlgInterpreter extends Alg[Id] {
def test(msg: String) = msg
}
implicit val idToOption = new (Id ~> Option) {
override def apply[A](a: Id[A]): Option[A] = Some{ a }
}
val idAlg = new AlgInterpreter
val optionAlg = idAlg.mapK(idToOption)
println(idAlg.test())
println(optionAlg.test())
When the default value of the msg argument of the test function is removed, the compilation will succeed.
Tried to tackle this one but didn't even find the place to change scalajs version. I suspect many things are hidden under sbt-catalysts magic.. I cound use a few pointers from someone who knows more.
Currently only autoFunctorK
and autoInvariantK
define fully refined instances in a special fullyRefined
object inside the companion object of algebras.
We have at least three options:
Generate only fully refined instances:
Add a fullyRefined
flag to macro annotations:
Automatically decide whether to generate fully refined instances based on the presence of type members:
The macro annotations in cats-tagless
are implemented using the macro system of scalameta.
However, the development of macros and macro annotations in Scala Meta was closed, as explained
here.
With these lessons learned, we decided to retire our efforts to build a macro system on top of Scalameta.
Current support for Scalameta macros may not work in next versions of the scala compiler.
On the other hand, the Scala compiler has recently incorporated the macro-paradise compiler plugin, which enables support for macro annotations based on scala-reflect
, into their main codebase.
The goal of this issue is to migrate existing macros to the scala.reflect
based macro annotations.
I spotted invalid link https://typelevel.org/cats-tagless/typeclasses.md#invariantK in docs
trait Example[F[_]] {
def unit: F[Unit]
def withFoo(foo: String): Example[F]
}
object Example {
import cats.tagless._
implicit val exampleFunctorK: FunctorK[Example] = Derive.functorK[Example]
}
Gives
Error:scalac: overriding method withFoo in trait Example of type (foo: String)mypkg.pkg.Example[G];
method withFoo has incompatible type
There's a trivial manual implementation here:
implicit val exampleFunctorK: FunctorK[Example] = new FunctorK[Example] {
def mapK[F[_], G[_]](af: Example[F])(fk: F ~> G): Example[G] =
new Example[G] {
def unit: G[Unit] = fk(af.unit)
def withFoo(foo: String): Example[G] =
exampleFunctorK.mapK(af.withFoo(foo))(fk)
}
}
}
e.g.
@autoFunctorK
trait Foo[F[_]]{
def int: F[Int]
}
//currently doesn't work
@autoFunctorK
trait Baz[F[_]]{
def foo: Foo[F]
}
@raulraja would you suggest anyone that could help with this?
The microsite with tut snippets is great, but those are not fully-fledged examples.
def invariantKApplicativeError[E]: InvariantK[ApplicativeError[*[_], E]] =
Derive.invariantK[ApplicativeError[*[_], E]]
The above results in:
could not find implicit value of type cats.tagless.InvariantK[[F[_]]cats.ApplicativeError.CatchOnlyPartiallyApplied[T,F,E]]
which doesn't seem too helpful...
An attempt to derive MonadError:
def invariantKMonadError[E]: InvariantK[MonadError[*[_], E]] =
Derive.invariantK[MonadError[*[_], E]]
yields
could not find implicit value of type cats.tagless.InvariantK[[F[_]]E => F[B]]
which is slightly more helpful. Implementing the instance helps, but not a lot:
implicit def functorKFunctionEffect[I, O]: FunctorK[Lambda[F[_] => I => F[O]]] = new FunctorK[Lambda[F[_] => I => F[O]]] {
def mapK[F[_], G[_]](af: I => F[O])(fk: F ~> G): I => G[O] = i => fk(af(i))
}
could not find implicit value of type cats.tagless.InvariantK[[F[_]]=> F[Boolean]]
and I implemented that too, but it's not being picked up:
could not find implicit value of type cats.tagless.InvariantK[[F[_]]=> F[Boolean]]
e.g.
FunctorK
for EitherT
, StateT
, WriterT
,Kleisli
InvariantK
for Functor
, Apply
, FlatMap
, etc...
I wonder if they are welcomed in the companion or separate module?
See Derive.applyK
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.