Question

I have something called a Generator:

trait Generator[A, B] {
  def generate(in: Seq[A]): Seq[B]
}

I can provide a Bind instance for this generator:

object Generator {
  implicit def generatorBind[T]: Bind[({type l[B] = Generator[T, B]})#l] = new Bind[({type l[B] = Generator[T, B]})#l] {

    def map[A, B](generator: Generator[T, A])(f: A => B): Generator[T, B] = new Generator[T, B] {
      def generate(in: Seq[T]): Seq[B] = generator.generate(in).map(f)
    }

    def bind[A, B](generator: Generator[T, A])(f: A =>Generator[T, B]): Generator[T, B] = new Generator[T, B] {
      def generate(in: Seq[T]): Seq[B] = generator.generate(in).flatMap(v => f(v).generate(in))
    }
  }
}

Unfortunately, type inference is completely lost if I try to use my generators as applicative instances:

val g1 = new Generator[Int, Int] { def generate(seq: Seq[Int]) = seq.map(_ + 1) }
val g2 = new Generator[Int, Int] { def generate(seq: Seq[Int]) = seq.map(_ + 10) }

// doesn't compile
// can make it compile with ugly type annotations
val g3 = ^(g1, g2)(_ / _)

My only workaround for now has been to add a specialised method to the Generator object:

def ^[T, A, B, C](g1: Generator[T, A], g2: Generator[T, B])(f: (A, B) => C) = 
  generatorBind[T].apply2(g1, g2)(f)

Then this compiles:

val g4 = Generator.^(g1, g2)(_ / _)

Is there a workaround for this problem? I suppose there is because using State[S, A] as a Monad poses the same kind of issue (but in Scalaz there seems to be a special treatment for State).

Was it helpful?

Solution

You can use ApplicativeBuilder if explicitly annotate g1 and g2 types, or change to abstract class Generator

// java.lang.Object with Generator[Int, Int] !!!
val badInference = new Generator[Int, Int] { def generate(seq: Seq[Int]) = seq.map(_ + 1) }

val g1: Generator[Int, Int] = new Generator[Int, Int] { def generate(seq: Seq[Int]) = seq.map(_ + 1) }
val g2: Generator[Int, Int] = new Generator[Int, Int] { def generate(seq: Seq[Int]) = seq.map(_ + 10) }
val g3 = (g1 |@| g2)(_ / _)

OTHER TIPS

I think implicit macro's fundep materialization (aka functional dependency) is to help this.

trait Iso[T, U] {
  def to(t: T) : U
  def from(u: U) : T
}
case class Foo(i: Int, s: String, b: Boolean)
def conv[C](c: C)(implicit iso: Iso[C, L]): L = iso.from(c)
val tp  = conv(Foo(23, "foo", true))

It requires macro paradise tho.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top