case class Zero()
case class Succ[N](x: N)
import Stream.{cons, continually}
trait ZipWith[N, S] {
type T
def manyApp: N => Stream[S] => T
def zipWith: N => S => T = n => f => manyApp(n)(continually(f))
}
object ZipWith {
implicit def ZeroZipWith[S] = new ZipWith[Zero, S] {
type T = Stream[S]
def manyApp = n => xs => xs
}
implicit def SuccZipWith[N, S, R](implicit zw: ZipWith[N, R]) =
new ZipWith[Succ[N],S => R] {
type T = Stream[S] => zw.T
def zapp[A, B](xs: Stream[A => B], ys: Stream[A]): Stream[B] = (xs, ys) match {
case (cons(f, fs), cons(s, ss)) => cons(f(s),zapp(fs, ss))
case (_, _) => Stream.empty
}
def manyApp = n => xs => ss => n match {
case Succ(i) => zw.manyApp(i)(zapp(xs, ss))
}
}
}
object Test {
def zWith[N, S](n: N, s: S)(implicit zw: ZipWith[N, S]): zw.T = zw.zipWith(n)(s)
def zipWith0: Stream[Int] = zWith(Zero(),0)
// (Stream[A]) => java.lang.Object with ZipWith[Zero,B]{type T = Stream[B]}#T
// should normalise to: Stream[A] => Stream[B]
def map[A, B](f: A => B) = zWith(Succ(Zero()),f)
def zipWith3[A, B, C, D](f: A => B => C => D) = //: Stream[A] => Stream[B] => Stream[C] => Stream[D] = // BUG why do we need a return type?
zWith(Succ(Succ(Succ(Zero()))),f)
}