SkillAgentSearch skills...

AUXify

Introduces macro/meta annotations @ aux, @ self, @ instance, @ apply, @ delegated, @ syntax and String-based type class LabelledGeneric

Install / Use

/learn @DmytroMitin/AUXify

README

AUXify

Build Status Maven Central Maven Central Sonatype Snapshots javadoc Scaladex Join the chat at https://gitter.im/DmytroMitin/AUXify

mvnrepository repo1.maven

Contents

Using AUXify-Shapeless

Write in build.sbt

scalaVersion := "2.13.3"
//scalaVersion := "2.12.11"
//scalaVersion := "2.11.12"
//scalaVersion := "2.10.7"

resolvers += Resolver.sonatypeRepo("public")

libraryDependencies ++= Seq(
  "com.github.dmytromitin" %% "auxify-shapeless" % [LATEST VERSION],
  "com.github.dmytromitin" %% "shapeless" % (CrossVersion.partialVersion(scalaVersion.value) match {
    case Some((2, v)) if v >= 11 => "2.4.0-M1-30032020-e6c3f71-PATCH"
    case _                       => "2.4.0-SNAPSHOT-18022020-bf55524-PATCH"
  })
)

Helps to overcome Shapeless limitation that shapeless.LabelledGeneric is Symbol-based rather than String-based.

Introduces type classes SymbolToString, StringToSymbol to convert between symbol singleton type and string singleton type

implicitly[StringToSymbol.Aux["a", Symbol @@ "a"]]
implicitly[SymbolToString.Aux[Symbol @@ "a", "a"]]
stringToSymbol("a") // returns Symbol("a") of type Symbol @@ "a"
symbolToString(Symbol("a")) // returns "a" of type "a"

and String-based type class com.github.dmytromitin.auxify.shapeless.LabelledGeneric

case class A(i: Int, s: String, b: Boolean)
implicitly[LabelledGeneric.Aux[A, Record.`"i" -> Int, "s" -> String, "b" -> Boolean`.T]]
LabelledGeneric[A].to(A(1, "a", true)) // field["i"](1) :: field["s"]("a") :: field["b"](true) :: HNil
LabelledGeneric[A].from(field["i"](1) :: field["s"]("a") :: field["b"](true) :: HNil) // A(1, "a", true)

Also there are convenient syntaxes

import com.github.dmytromitin.auxify.shapeless.hlist._
import StringsToSymbols.syntax._
("a".narrow :: "b".narrow :: "c".narrow :: HNil).stringsToSymbols // 'a.narrow :: 'b.narrow :: 'c.narrow :: HNil
import SymbolsToStrings.syntax._
('a.narrow :: 'b.narrow :: 'c.narrow :: HNil).symbolsToStrings // "a".narrow :: "b".narrow :: "c".narrow :: HNil

import com.github.dmytromitin.auxify.shapeless.coproduct._
import StringsToSymbols.syntax._
(Inr(Inr(Inl("c".narrow))) : "a" :+: "b" :+: "c" :+: CNil).stringsToSymbols // Inr(Inr(Inl('c.narrow))) : (Symbol @@ "a") :+: (Symbol @@ "b") :+: (Symbol @@ "c") :+: CNil
import SymbolsToStrings.syntax._
(Inr(Inr(Inl('c.narrow))) : (Symbol @@ "a") :+: (Symbol @@ "b") :+: (Symbol @@ "c") :+: CNil).symbolsToStrings // Inr(Inr(Inl("c".narrow))) : "a" :+: "b" :+: "c" :+: CNil

import com.github.dmytromitin.auxify.shapeless.record._
import StringsToSymbols.syntax._
(field["a"](1) :: field["b"]("s") :: field["c"](true) :: HNil).stringsToSymbols // field[Symbol @@ "a"](1) :: field[Symbol @@ "b"]("s") :: field[Symbol @@ "c"](true) :: HNil
import SymbolsToStrings.syntax._
(field[Symbol @@ "a"](1) :: field[Symbol @@ "b"]("s") :: field[Symbol @@ "c"](true) :: HNil).symbolsToStrings // field["a"](1) :: field["b"]("s") :: field["c"](true) :: HNil

import com.github.dmytromitin.auxify.shapeless.union._
import StringsToSymbols.syntax._
(Inr(Inr(Inl(field["c"](true)))): Union.`"a" -> Int, "b" -> String, "c" -> Boolean`.T).stringsToSymbols // Inr(Inr(Inl(field[Witness.`'c`.T](true)))): Union.`'a -> Int, 'b -> String, 'c -> Boolean`.T
import SymbolsToStrings.syntax._
(Inr(Inr(Inl(field[Symbol @@ "c"](true)))): Union.`'a -> Int, 'b -> String, 'c -> Boolean`.T).symbolsToStrings // Inr(Inr(Inl(field[Witness.`"c"`.T](true)))): Union.`"a" -> Int, "b" -> String, "c" -> Boolean`.T

You can play with AUXify online at Scastie: https://scastie.scala-lang.org/r52fCgloRc2VVM5FnNmbsQ

Using AUXify-Macros

Write in build.sbt

scalaVersion := "2.13.3"
//scalaVersion := "2.12.11"
//scalaVersion := "2.11.12"
//scalaVersion := "2.10.7"

resolvers += Resolver.sonatypeRepo("public")

libraryDependencies += "com.github.dmytromitin" %% "auxify-macros" % [LATEST VERSION]

scalacOptions += "-Ymacro-annotations" // in Scala >= 2.13
//addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full) // in Scala <= 2.12

@aux (helper for type refinement)

Transforms

@aux
trait Add[N <: Nat, M <: Nat] {
  type Out <: Nat
  def apply(n: N, m: M): Out
}

into

trait Add[N <: Nat, M <: Nat] {
  type Out <: Nat
  def apply(n: N, m: M): Out
}

object Add {
  type Aux[N <: Nat, M <: Nat, Out0 <: Nat] = Add[N, M] { type Out = Out0 }
}

So it can be used:

implicitly[Add.Aux[_2, _3, _5]]

Convenient for type-level programming.

@self

Transforms

@self
sealed trait Nat {
  type ++ = Succ[Self]
}

@self
case object _0 extends Nat 

type _0 = _0.type

@self
case class Succ[N <: Nat](n: N) extends Nat

into

sealed trait Nat { self =>
  type Self >: self.type <: Nat { type Self = self.Self }
  type ++ = Succ[Self]
}

case object _0 extends Nat {
  override type Self = _0
}

type _0 = _0.type

case class Succ[N <: Nat](n: N) extends Nat {
  override type Self = Succ[N]
}

Convenient for type-level programming.

Generating lower bound >: self.type and/or F-bound type Self = self.Self for trait can be switched off

@self(lowerBound = false, fBound = false)

@instance (constructor)

Transforms

@instance
trait Monoid[A] {
  def empty: A
  def combine(a: A, a1: A): A
}

into

trait Monoid[A] {
  def empty: A
  def combine(a: A, a1: A): A
}

object Monoid {
  def instance[A](f: => A, f1: (A, A) => A): Monoid[A] = new Monoid[A] {
    override def empty: A = f
    override def combine(a: A, a1: A): A = f1(a, a1)
  }
}

So it can be used

implicit val intMonoid: Monoid[Int] = instance(0, _ + _)

Polymorphic methods are not supported (since Scala 2 lacks polymorphic functions).

@apply (materializer)

Transforms

@apply
trait Show[A] {
  def show(a: A): String
}

into

trait Show[A] {
  def show(a: A): String
}

object Show {
  def apply[A](implicit inst: Show[A]): Show[A] = inst
}

So it can be used

Show[Int].show(10)

Method materializing type class can return more precise type than the one of implicit to be found (like the in Shapeless or summon in Dotty). For example

@apply
trait Add[N <: Nat, M <: Nat] {
  type Out <: Nat
  def apply(n: N, m: M): Out
}

is transformed into

trait Add[N <: Nat, M <: Nat] {
  type Out <: Nat
  def apply(n: N, m: M): Out
}

object Add {
  def apply[N <: Nat, M <: Nat](implicit inst: Add[N, M]): Add[N, M] { type Out = inst.Out } = inst
}

Simulacrum annotation @typeclass also generates, among other, materializer but doesn't support type classes with multiple type parameters.

@delegated

Generates methods in companion object delegating to implicit instance of trait (type class).

Transforms

@delegated
trait Show[A] {
  def show(a: A): String
}

into

trait Show[A] {
  def show(a: A): String
}

object Show {
  def show[A](a: A)(implicit inst: Show[A]): String = inst.show(a)
}

So it can be used

Show.show(10)

@syntax

Transforms

@syntax
trait Monoid[A] {
  def empty: A
  def combine(a: A, a1: A): A
}

into

trait Monoid[A] {
  def empty: A
  def combine(a: A, a1: A): A
}

object Monoid {
  object syntax {
    implicit class Ops[A](a: A) {
      def combine(a1: A)(implicit inst: Monoid[A]): A = inst.combine(a, a1)
    }
  }
}

So it can be used

import Monoid.syntax._
2 combine 3

Simulacrum annotation @typeclass also generates syntax but doesn't support type classes with multiple type parameters.

Inheritance of type classes is not supported (anyway it's [broken](https://typelevel.org/blog

View on GitHub
GitHub Stars29
CategoryDevelopment
Updated1y ago
Forks3

Languages

Scala

Security Score

80/100

Audited on Feb 11, 2025

No findings