Git Product home page Git Product logo

scalaz-plugin's Issues

Detect propositions

https://ncatlab.org/nlab/show/mere+proposition

Like typeclasses, they are unique but not by an ad-hoc convention, but rather by definition. Some examples: Void, Unit, A === B, A <~< B, IsCovariant[F], Inhabited[A], A => Void, Functor[F], Eq[A], and also tuples of propositions. Anything that is isomorphic to a proposition is a proposition.

We are interested not just in propositions, but in those propositions that do not have computational content (i.e. not Functor[F] or Eq[A]). They can be forcibly asserted in any part of our program, e.g. implicitly[A === A].asInstanceOf[A === B]. This allows us to replace any complicated expression returning complicated : P with a simpler expression assertP : P, assuming totality. For example, if you have an inductive proof that commutativity of natural number addition holds, you can erase the inductive part and straight up force the result.

plusIsCommutative : (a : Nat) -> (b : Nat) -> plus a b = plus b a
plusIsCommutative a b = ... -- some recursive calls
-- can be replaced to
plusIsCommutative a b = assertTrue

Every single method on Is can be replaced with unsafeForce[A, B], which just asserts the result (and with polyopt it will be a val).

Polyopt causes Types$TypeError

trait DisjunctionFunctions
trait MaybeFunctions {
  def empty[A]: Option[A]      = None
}

trait AllFunctions extends DisjunctionFunctions with MaybeFunctions
object Scalaz extends AllFunctions
% scalac polyopt_scalaz.scala
[running phase parser on polyopt_scalaz.scala]
[Not checkable: parser]
[running phase namer on polyopt_scalaz.scala]
[Not checkable: namer]
[running phase packageobjects on polyopt_scalaz.scala]
[Not checkable: packageobjects]
[running phase typer on polyopt_scalaz.scala]
[Now checking: typer]
[running phase patmat on polyopt_scalaz.scala]
[Now checking: patmat]
[running phase scalaz-orphans on polyopt_scalaz.scala]
[Now checking: scalaz-orphans]
[running phase scalaz-polyopt on polyopt_scalaz.scala]
polyopt_scalaz.scala:7: error: scala.reflect.internal.Types$TypeError: MaybeFunctions does not name a parent class of module class Scalaz
	at scala.tools.nsc.typechecker.Contexts$ThrowingReporter.handleError(Contexts.scala:1434)
	at scala.tools.nsc.typechecker.Contexts$ContextReporter.issue(Contexts.scala:1281)
	at scala.tools.nsc.typechecker.Contexts$Context.issue(Contexts.scala:584)
	at scala.tools.nsc.typechecker.ContextErrors$ErrorUtils$.issueTypeError(ContextErrors.scala:106)
	at scala.tools.nsc.typechecker.ContextErrors$ErrorUtils$.issueNormalTypeError(ContextErrors.scala:99)
	at scala.tools.nsc.typechecker.ContextErrors$TyperContextErrors$TyperErrorGen$.MixinMissingParentClassNameError(ContextErrors.scala:336)
	at scala.tools.nsc.typechecker.Typers$Typer.findMixinSuper$1(Typers.scala:4888)
	at scala.tools.nsc.typechecker.Typers$Typer.typedSuper$1(Typers.scala:4900)
	at scala.tools.nsc.typechecker.Typers$Typer.typedOutsidePatternMode$1(Typers.scala:5569)
	at scala.tools.nsc.typechecker.Typers$Typer.typedInAnyMode$1(Typers.scala:5596)
	at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5603)
	at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5640)
	at scala.tools.nsc.typechecker.Typers$Typer.typedInternal(Typers.scala:5672)
	at scala.tools.nsc.typechecker.Typers$Typer.body$2(Typers.scala:5613)
	at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5618)
	at scala.tools.nsc.typechecker.Typers$Typer.typedQualifier(Typers.scala:5723)
	at scala.tools.nsc.typechecker.Typers$Typer.typedQualifier(Typers.scala:5729)
	at scala.tools.nsc.typechecker.Typers$Typer.typedSelectOrSuperCall$1(Typers.scala:5063)
	at scala.tools.nsc.typechecker.Typers$Typer.typedInAnyMode$1(Typers.scala:5587)
	at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5603)
	at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5640)
	at scala.tools.nsc.typechecker.Typers$Typer.typedInternal(Typers.scala:5672)
	at scala.tools.nsc.typechecker.Typers$Typer.body$2(Typers.scala:5613)
	at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5618)
	at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5717)
	at scala.tools.nsc.typechecker.Typers$Typer.typedTypeApply$1(Typers.scala:5376)
	at scala.tools.nsc.typechecker.Typers$Typer.typedOutsidePatternMode$1(Typers.scala:5563)
	at scala.tools.nsc.typechecker.Typers$Typer.typedInAnyMode$1(Typers.scala:5596)
	at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5603)
	at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5640)
	at scala.tools.nsc.typechecker.Typers$Typer.typedInternal(Typers.scala:5672)
	at scala.tools.nsc.typechecker.Typers$Typer.body$2(Typers.scala:5613)
	at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5618)
	at scala.tools.nsc.typechecker.Typers$Typer.typedQualifier(Typers.scala:5723)
	at scala.tools.nsc.typechecker.Typers$Typer.typedQualifier(Typers.scala:5729)
	at scala.tools.nsc.typechecker.Typers$Typer.typedSelectOrSuperCall$1(Typers.scala:5063)
	at scala.tools.nsc.typechecker.Typers$Typer.typedInAnyMode$1(Typers.scala:5587)
	at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5603)
	at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5640)
	at scala.tools.nsc.typechecker.Typers$Typer.typedInternal(Typers.scala:5672)
	at scala.tools.nsc.typechecker.Typers$Typer.body$2(Typers.scala:5613)
	at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5618)
	at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5717)
	at scala.tools.nsc.typechecker.Typers$Typer.typedTypeApply$1(Typers.scala:5376)
	at scala.tools.nsc.typechecker.Typers$Typer.typedOutsidePatternMode$1(Typers.scala:5563)
	at scala.tools.nsc.typechecker.Typers$Typer.typedInAnyMode$1(Typers.scala:5596)
	at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5603)
	at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5640)
	at scala.tools.nsc.typechecker.Typers$Typer.typedInternal(Typers.scala:5672)
	at scala.tools.nsc.typechecker.Typers$Typer.body$2(Typers.scala:5613)
	at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5618)
	at scala.tools.nsc.typechecker.Typers$Typer.transformedOrTyped(Typers.scala:5854)
	at scala.tools.nsc.typechecker.Typers$Typer.typedValDefImpl(Typers.scala:2087)
	at scala.tools.nsc.typechecker.Typers$Typer.typedValDef(Typers.scala:2043)
	at scalaz.meta.plugin.PolymorphicFunctionOptimizer$MyTransformer.createSuperCall(PolymorphicFunctionOptimizer.scala:132)
	at scalaz.meta.plugin.PolymorphicFunctionOptimizer$MyTransformer.$anonfun$processBody$8(PolymorphicFunctionOptimizer.scala:216)
	at scala.collection.immutable.List.flatMap(List.scala:335)
	at scalaz.meta.plugin.PolymorphicFunctionOptimizer$MyTransformer.processBody(PolymorphicFunctionOptimizer.scala:215)
	at scalaz.meta.plugin.PolymorphicFunctionOptimizer$MyTransformer.transform(PolymorphicFunctionOptimizer.scala:231)
	at scalaz.meta.plugin.PolymorphicFunctionOptimizer$MyTransformer.transform(PolymorphicFunctionOptimizer.scala:62)
	at scala.reflect.api.Trees$Transformer.$anonfun$transformStats$1(Trees.scala:2589)
	at scala.reflect.api.Trees$Transformer.transformStats(Trees.scala:2587)
	at scala.reflect.internal.Trees.$anonfun$itransform$7(Trees.scala:1447)
	at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:30)
	at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:25)
	at scala.reflect.internal.Trees.itransform(Trees.scala:1447)
	at scala.reflect.internal.Trees.itransform$(Trees.scala:1357)
	at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:18)
	at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:18)
	at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
	at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.super$transform(TypingTransformers.scala:40)
	at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.$anonfun$transform$2(TypingTransformers.scala:42)
	at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:30)
	at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:25)
	at scalaz.meta.plugin.PolymorphicFunctionOptimizer$MyTransformer.transform(PolymorphicFunctionOptimizer.scala:233)
	at scalaz.meta.plugin.PolymorphicFunctionOptimizer$MyTransformer.transform(PolymorphicFunctionOptimizer.scala:62)
	at scala.tools.nsc.ast.Trees$Transformer.transformUnit(Trees.scala:140)
	at scala.tools.nsc.transform.Transform$Phase.apply(Transform.scala:30)
	at scala.tools.nsc.Global$GlobalPhase.$anonfun$applyPhase$1(Global.scala:426)
	at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:419)
	at scala.tools.nsc.Global$GlobalPhase.$anonfun$run$1(Global.scala:390)
	at scala.tools.nsc.Global$GlobalPhase.$anonfun$run$1$adapted(Global.scala:390)
	at scala.collection.Iterator.foreach(Iterator.scala:944)
	at scala.collection.Iterator.foreach$(Iterator.scala:944)
	at scala.collection.AbstractIterator.foreach(Iterator.scala:1432)
	at scala.tools.nsc.Global$GlobalPhase.run(Global.scala:390)
	at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1446)
	at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1430)
	at scala.tools.nsc.Global$Run.compileSources(Global.scala:1423)
	at scala.tools.nsc.Global$Run.compile(Global.scala:1539)
	at scala.tools.partest.nest.DirectCompiler.execCompile$1(DirectCompiler.scala:114)
	at scala.tools.partest.nest.DirectCompiler.compile(DirectCompiler.scala:123)
	at scala.tools.partest.nest.Runner.attemptCompile(Runner.scala:502)
	at scala.tools.partest.nest.Runner$ScalaAndJava.result$lzycompute(Runner.scala:560)
	at scala.tools.partest.nest.Runner$ScalaAndJava.result(Runner.scala:560)
	at scala.tools.partest.nest.Runner$CompileRound.isOk(Runner.scala:546)
	at scala.tools.partest.nest.Runner.$anonfun$runTestCommon$3(Runner.scala:591)
	at scala.runtime.java8.JFunction0$mcZ$sp.apply(JFunction0$mcZ$sp.java:12)
	at scala.tools.partest.nest.Runner.nextTestAction(Runner.scala:145)
	at scala.tools.partest.nest.Runner.nextTestActionExpectTrue(Runner.scala:150)
	at scala.tools.partest.nest.Runner.$anonfun$runTestCommon$2(Runner.scala:591)
	at scala.tools.partest.nest.Runner.$anonfun$runTestCommon$2$adapted(Runner.scala:591)
	at scala.collection.LinearSeqOptimized.forall(LinearSeqOptimized.scala:81)
	at scala.collection.LinearSeqOptimized.forall$(LinearSeqOptimized.scala:78)
	at scala.collection.immutable.List.forall(List.scala:86)
	at scala.tools.partest.nest.Runner.$anonfun$runTestCommon$1(Runner.scala:591)
	at scala.tools.partest.nest.Runner.runInContext(Runner.scala:478)
	at scala.tools.partest.nest.Runner.runTestCommon(Runner.scala:591)
	at scala.tools.partest.nest.Runner.run(Runner.scala:677)
	at scala.tools.partest.nest.SuiteRunner.liftedTree1$1(Runner.scala:815)
	at scala.tools.partest.nest.SuiteRunner.runTest(Runner.scala:815)
	at scala.tools.partest.nest.SuiteRunner.$anonfun$runTestsForFiles$2(Runner.scala:833)
	at scala.tools.partest.package$$anon$2.call(package.scala:134)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

object Scalaz extends AllFunctions

Prefer vals when choosing a typeclass instance.

Currently typeclass resolution just asks for allImplicits and then chooses the first successful one. That could result in inefficient code.

  • prefer vals over defs or complex expressions.
  • prefer vals with the shortest prefix.
  • prefer vals that are local to the method.

Make mixins component a little bit smarter

Consider this case:

implicit def contravariant1[F[_]: Contravariant, G[_]: Functor]: Contravariant[[x] F[G[x]]]
implicit def contravariant2[F[_]: Functor, G[_]: Contravariant]: Contravariant[[x] F[G[x]]]
implicit def divisible1[F[_]: Divisible, G[_]: Applicative]: Divisible[[x] F[G[x]]]
implicit def divisible2[F[_]: Applicative, G[_]: Divisible]: Divisible[[x] F[G[x]]]

(not sure about the divisible instances, but the point stands)

Sufficiency checker complains about a valid instance.

import scalaz.meta.minimal

trait InvariantFunctor[F[_]] {
  def imap[A, B](ma: F[A])(f: A => B)(g: B => A): F[B]
}

trait Functor[F[_]] extends InvariantFunctor[F] {
  def map[A, B](ma: F[A])(f: A => B): F[B]

  def imap[A, B](ma: F[A])(f: A => B)(g: B => A): F[B] = map(ma)(f)
}

trait Apply[F[_]] extends Functor[F] {
  def ap[A, B](fa: F[A])(f: F[A => B]): F[B]
}

@minimal(("pure", "ap"), ("unit", "zip", "map"))
trait Applicative[F[_]] extends Apply[F] {
  def unit: F[Unit] = pure(())

  def pure[A](a: A): F[A] = map(unit)(_ => a)

  def zip[A, B](fa: F[A], fb: F[B]): F[(A, B)] = ap(fa)(map(fb)(b => a => (a, b)))

  def ap[A, B](fa: F[A])(f: F[A => B]): F[B] = map(zip(f, fa)) { case (f, a) => f(a) }

  def map[A, B](ma: F[A])(f: A => B): F[B] = ap(ma)(pure(f))
}

object ComposeImpl {
  type Compose[F[_], G[_]] = { type L[X] = F[G[X]] }

  def invariant[F[_], G[_]](implicit F0: InvariantFunctor[F], G0: InvariantFunctor[G]): InvariantFunctor[Compose[F, G]#L] =
    new ComposeInvariantFunctor[F, G] {
      val F = F0
      val G = G0
    }

  def functor[F[_], G[_]](implicit F0: Functor[F], G0: Functor[G]): Functor[Compose[F, G]#L] =
    new ComposeFunctor[F, G] {
      val F = F0
      val G = G0
    }

  def apply[F[_], G[_]](implicit F0: Apply[F], G0: Apply[G]): Apply[Compose[F, G]#L] =
    new ComposeApply[F, G] {
      val F = F0
      val G = G0
    }

  def applicative[F[_], G[_]](implicit F0: Applicative[F], G0: Applicative[G]): Applicative[Compose[F, G]#L] =
    new ComposeApplicative[F, G] {
      val F = F0
      val G = G0
    }

  private trait ComposeInvariantFunctor[F[_], G[_]] extends InvariantFunctor[Compose[F, G]#L] {
    val F: InvariantFunctor[F]
    val G: InvariantFunctor[G]

    final def imap[A, B](ma: F[G[A]])(f: A => B)(g: B => A): F[G[B]] =
      F.imap[G[A], G[B]](ma)(G.imap(_)(f)(g))(G.imap(_)(g)(f))
  }

  private trait ComposeFunctor[F[_], G[_]] extends Functor[Compose[F, G]#L] {
    val F: Functor[F]
    val G: Functor[G]

    final def map[A, B](fa: F[G[A]])(f: A => B): F[G[B]] =
      F.map(fa)(G.map(_)(f))
  }

  private trait ComposeApply[F[_], G[_]] extends ComposeFunctor[F, G] with Apply[Compose[F, G]#L] {
    val F: Apply[F]
    val G: Apply[G]

    final def ap[A, B](fa: F[G[A]])(f: F[G[A => B]]): F[G[B]] =
      F.ap(fa)(F.map(f)(gab => G.ap(_)(gab)))
  }

  private trait ComposeApplicative[F[_], G[_]] extends ComposeApply[F, G] with Applicative[Compose[F, G]#L] {
    val F: Applicative[F]
    val G: Applicative[G]

    final override def pure[A](a: A): F[G[A]] = F.pure(G.pure(a))
  }
}

Sufficiency checker complains missing implementation for methods: ap OR (unit AND zip AND map) on new ComposeApplicative[F, G].

Rewriting

Possible syntax:

trait Functor[F[_]] {
  def map[A, B](fa: F[A])(f: A => B): F[B]
}
object Functor {
  def fusion[F[_], A, B, C](fa: F[A], f: A => B, g: B => C)
      (implicit F: Functor[F], R: Rewrites): Rewrite =
    F.map(F.map(fa)(f))(g) rewriteTo F.map(fa)(f andThen g)
}

///////////////////////////////////
///////////////////////////////////

class rewrite extends annotation.StaticAnnotation
final class Rewrite private()
final class Rewrites private ()
implicit class RewriteSyntax[A](val from: A) {
  def rewriteTo(to: A)(implicit R: Rewrites): Rewrite = ???
}

R : Rewrites is there to guarantee that fusion won't be called by the user.

Spurious unused import when compiling Scalaz8

A very long and arduous investigation showed that our existing method of replacing global.analyzer wasn't robust enough, and some of the old instances were still floating around. Since analyzer contained a whole bunch of mutable variables, that resulted in some imports not being recorded as used.

NPE in polyopt on standalone expressions

All of these (fuzzing) cause an NPE:

object I0 {
(null: Any) match {
case _ =>
}
}

object I0 {
val I0 = 0
+ 0
0
}

object I0 {
(1, 2) match { case _ => } }

object I0 {
val I0: Int = 2
}
object i1 {
(null: Any) match {
case toInt: Int => println()
case None =>
}
}

object i0 {
lazy val I1 = 1
-2 + 2
}

object i0 {
<i1/> }

object I0 {
val i1 = 0
val i2 = 1
i1 < 2
-4 :: 3 :: Nil
}

package i0
package i0 {
object i1 {
class i1
null
}
}

object I0 {
val i1: Int = 2
}
object i1 {
(null: Any) match {
case toInt: Int => println()
case None =>
}
}

object i0 {
def i1[@specialized(Int) i1](I2: i1) = new Object
0;
}

object i0 {
val i0 = new { 42 }
}

object i0 {
(null: Any) match {
case 1 | 2 | 2 | 2 => i0
}
def main(i1: Array[String]): Unit = {}
}

object I0 {
type I1 = (Integer => Int)
}
object I2 {
(??? : AnyRef)
}
i44.scala:1: error: java.lang.NullPointerException
	at scalaz.meta.plugin.PolymorphicFunctionOptimizer$MyTransformer$$anonfun$1.applyOrElse(PolymorphicFunctionOptimizer.scala:173)
	at scalaz.meta.plugin.PolymorphicFunctionOptimizer$MyTransformer$$anonfun$1.applyOrElse(PolymorphicFunctionOptimizer.scala:171)
	at scala.collection.immutable.List.collect(List.scala:305)
	at scalaz.meta.plugin.PolymorphicFunctionOptimizer$MyTransformer.processBody(PolymorphicFunctionOptimizer.scala:171)
	at scalaz.meta.plugin.PolymorphicFunctionOptimizer$MyTransformer.transform(PolymorphicFunctionOptimizer.scala:226)
	at scalaz.meta.plugin.PolymorphicFunctionOptimizer$MyTransformer.transform(PolymorphicFunctionOptimizer.scala:57)
	at scala.reflect.api.Trees$Transformer.$anonfun$transformStats$1(Trees.scala:2589)
	at scala.reflect.api.Trees$Transformer.transformStats(Trees.scala:2587)

Optimize Option[A] as A | Null

As discussed with @Odomontois on Telegram, in non-polymorphic context, Option[A] can be replaced with A | Null (primitives would have to be replaced with their Java counterparts in Scala2).

Consider the following function, that computes the maximum value of all odd numbers in a list:

def maxOdd(l: List[Int]): Option[Int] = {
  def go(l: List[Int], sum: Option[Int]): Option[Int] = l match {
    case Nil => sum
    case x :: xs if x % 2 == 0 => go(xs, sum)
    case x :: xs => sum match {
      case None => go(xs, Some(x))
      case Some(y) => go(xs, Some(x max y))
    }
  }

  go(l, None)
}

We can optimize it as (Integer here plays the role of Int | Null):

def maxOdd(l: List[Int]): Option[Int] = {
  def go(l: List[Int], sum: Integer): Integer = l match {
    case Nil => sum
    case x :: xs if x % 2 == 0 => go(xs, sum)
    case x :: xs => sum match {
      case null => go(xs, x)
      case y => go(xs, x max y)
    }
  }

  Option(go(l, null))
}

This sort of rewriting should only be applied locally and to private functions.

Instance instantiation in the outermost scope

For future docs:

Instance instantiation in the outermost scope

Consider the following snippet:

implicit def eqList[A](implicit A: Eq[A]): Eq[List[A]] =
  new Eq[A] { ... }
implicit class EqOps[A](value: A) {
  def ===(other: A)(implicit A: Eq[A]): Boolean = A.equal(value, other)
}
...
object Test {
  def test: Unit = {
    val a : List[Int] = ...
    val b : List[Int] = ...

    for (i < 0 until 100) {
      a === b
    }
  }
}

The body of the loop will compile to:

new EqOps(a).===(b)(eqList(eqInt))

So there are two allocations per comparison, one allocation of the syntax extension class and one for the instance of Eq[List[A]]. This optimization should fix the latter by instantiating the instance in the outermost static scope,

object Test {
  implicit val $ev1: Eq[List[A]] = eqList(eqInt)

  def test: Unit = {
    val a : List[Int] = ...
    val b : List[Int] = ...

    for (i < 0 until 100) {
      new EqOps(a).===(b)($ev1)
    }
  }
}

or at the beginning of the function:

class Test {
  def test: Unit = {
    implicit val $ev1: Eq[List[A]] = eqList(eqInt)
    val a : List[Int] = ...
    val b : List[Int] = ...

    for (i < 0 until 100) {
      new EqOps(a).===(b)($ev1)
    }
  }
}

Requirements

  • No side-effects in implicits.

Singleton improvements

Convince Scalac that all instances of a typeclass or a proposition have the same singleton type. Could be crucial for associated types...

Convince Scalac that val x = ...; val y = x implies x.type = y.type.

Syntax for ADTs?

Kinda like the dotty enum proposal but different. Should look at that for syntax ideas.

Pattern match optimization.

In scalaz/scalaz#1958 @fommil uses this optimization:

fa match {
  case Right(b) => f(b)
  case Left(a) => Left(a)
}
fa match {
  case Right(b) => f(b)
  case a => a.asInstanceOf[Either[L, B]]
}

perhaps we could detect this particular case and optimize it automatically.

Pattern functors, algebras, folds, catas, free structures for free

trait Monoid[A] extends Typeclass {
  def empty: A
  def combine(a: A, b: A): A

  // Law syntax is not yet decided.
  def leftIdentity(a: A): Law  = combine(empty, a) is a
  def rightIdentity(a: A): Law = combine(a, empty) is a
  def associativity(a: A, b: A, c: A): Law = 
    combine(a, combine(b, c)) is combine(combine(a, b), c)
}
object Monoid {
  @free(Monoid) type Free[A]
}
// rewrite to:
object Monoid {
  sealed class Free[A]
  object Free {
    implicit def instance: Monoid[Free[A]] = ...
    def lift[A](a: A): Free[A] = ...
    def cata[A](f: A => B)(implicit B: Monoid[A]): B = ...
  }
}

Using the laws to simplify the structure seems a little bit tricky, but not impossible. One simple heuristic is to use any laws that reduce the total depth. In this case it's left and right identity laws.

Can we generate scott (Scott encoding, peeling one layer at a time) as well? i.e.

def scott[Z](f: Scott[Free[A], Z]): Z

trait Scott[A, Z] {
  def empty: Z
  def combine(a: A, b: A): Z

  // How to rewrite laws?
}

If Alg in @free(Alg) type Free[A] is not a typeclass, don't use implicit.

ST fusion

The best performing IO or ST is still tremendously slower than impure code.

From twitter:

For each def foo(args: ...): ST[S, A], create an impure def foo$ST(args...): A. Whenever you do ST { body } flatMap(foo(...)) rewrite it as ST { val a = body; foo$ST(args) }. This way you can remove all or almost all trampoline jumps in non-higher order ST code. Finally at the end rewrite runST { ST { foo } } into foo$ST. Cross-module fusion will only work for non-HO code, but arguably that's the majority of IO and ST code anyway.

Note that this can not be done with a simple rewriting system, since we need to generate methods.

Better UX

  • Publish the plugin
  • Add options to disable/enable each feature.
  • Make sure that error messages are sound.

Using scalaz-plugin in sbt 1.2.x project gives unresolved dependency

I'm not extremely certain that my issue is related to using sbt 1.2.8; but based on the error that's my best guess as I am using Scala 2.12.6 . Currently with sbt 1.2.8, I'm getting this with addSbtPlugin("org.scalaz" % "scalaz-plugin" % "0.0.7"):

[warn]  module not found: org.scalaz#scalaz-plugin;0.0.7
[warn] ==== typesafe-ivy-releases: tried
[warn]   https://repo.typesafe.com/typesafe/ivy-releases/org.scalaz/scalaz-plugin/scala_2.12/sbt_1.0/0.0.7/ivys/ivy.xml
[warn] ==== sbt-plugin-releases: tried
[warn]   https://repo.scala-sbt.org/scalasbt/sbt-plugin-releases/org.scalaz/scalaz-plugin/scala_2.12/sbt_1.0/0.0.7/ivys/ivy.xml
[warn] ==== local: tried
[warn]   /home/brandon/.ivy2/local/org.scalaz/scalaz-plugin/scala_2.12/sbt_1.0/0.0.7/ivys/ivy.xml
[warn] ==== public: tried
[warn]   https://repo1.maven.org/maven2/org/scalaz/scalaz-plugin_2.12_1.0/0.0.7/scalaz-plugin-0.0.7.pom
[warn] ==== local-preloaded-ivy: tried
[warn]   /home/brandon/.sbt/preloaded/org.scalaz/scalaz-plugin/0.0.7/ivys/ivy.xml
[warn] ==== local-preloaded: tried
[warn]   file:////home/brandon/.sbt/preloaded/org/scalaz/scalaz-plugin_2.12_1.0/0.0.7/scalaz-plugin-0.0.7.pom
[warn]  ::::::::::::::::::::::::::::::::::::::::::::::
[warn]  ::          UNRESOLVED DEPENDENCIES         ::
[warn]  ::::::::::::::::::::::::::::::::::::::::::::::
[warn]  :: org.scalaz#scalaz-plugin;0.0.7: not found
[warn]  ::::::::::::::::::::::::::::::::::::::::::::::
[warn] 
[warn]  Note: Some unresolved dependencies have extra attributes.  Check that these dependencies exist with the requested attributes.
[warn]          org.scalaz:scalaz-plugin:0.0.7 (scalaVersion=2.12, sbtVersion=1.0)
[warn] 
[warn]  Note: Unresolved dependencies path:
[warn]          org.scalaz:scalaz-plugin:0.0.7 (scalaVersion=2.12, sbtVersion=1.0) (/home/brandon/workspace/CCRS/project/plugins.sbt#L5-6)
[warn]            +- default:ccrs-build:0.1.0-SNAPSHOT (scalaVersion=2.12, sbtVersion=1.0)
[error] sbt.librarymanagement.ResolveException: unresolved dependency: org.scalaz#scalaz-plugin;0.0.7: not found

I noticed there were some recent updates that might address the issue.

Would it be possible to cut a new release if this is likely resolved, and if not, what else can I do to help identify and correct the issue?

Thanks, and looking forward to trying scalaz-plugin (though I wonder if it will even be possible to use on my partly-legacy code base!)

Test with the fuzzer

There are most certainly some weird corner cases that I missed in polyopt and orphan checking, which would likely result in a MatchError during compilation. We can insert extends Typeclass, <: Typeclass, @minimal(...), and @orphan all over the place in the fuzzer output and see what we missed.

Investigate lenses

AFAIK lenses don't work well because of type / implicit inference issues. Can we fix those problems?

Come up with new module syntax

The current module encoding in Scalaz 8 is great performance-wise, but is incomprehensible and ugly. Note that modules in Scalaz 8 may contain more than one type, but generally do not, so a newtyping mechanism would suffice to fix the vast majority of ugliness.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.