Open rubenfiszel opened 7 years ago
I had implemented a version of this a while ago. I ultimately dropped it because it made writing rewrite rules a tad more annoying.
Consider the possible syntax:
// Check the global rewrite rule list for rewrites on And given these inputs. Stage if none are available.
@internal def bool_and(x: Exp[Bool], y: Exp[Bool]) = rewrite[And](x, y).getOrElse(stage(And(x,y))(ctx))
// Create rewrite rule and append to the global list of rewrite rules
rewrite[And]{case (Const(x: Boolean), Const(y: Boolean)) => bool(x && y) }
One problem here is that the partial function doesn't have access to the SourceContext. I don't believe you can have implicit arguments to a partial function, so the only way to get this to work, so the actual syntax here would be:
rewrite[And]{case (Const(x: Boolean), Const(y: Boolean), ctx: SrcCtx) => bool(x && y)(ctx) }
As a more minor point, unlike overriding constructors, the case statements always have to explicitly state the types, as they can't be inferred otherwise.
Alternatively, the partial functions can be of type PartialFunction[Any, SrcCtx => Exp[_]]
. That might work? When I tried this before, I wound up creating two forms of rewrite rules, those of the form PartialFunction[Any, Exp[_]]
and those of form PartialFunction[Any, SrcCtx => Exp[_]]
. It worked, but it was just a tad awkward to separate like that.
We could always use an implicit conversion:
implicit def eatSC(x:Exp[_]) = (sc: SourceContext) => x
or a similar overloaded rewrite function
It would probably have to be defined on PartialFunction, i.e.
implicit def addSC(f: PartialFunction[Any, Exp[_]]): PartialFunction[Any, SrcCtx => Exp[_]] = ...
Are you sure ?
scala> case class Const[T](x: T)
defined class Const
scala> implicit def toConst(x: Int):Const[Int] = Const(x)
toConst: (x: Int)Const[Int]
scala> var x = PartialFunction.empty[Any, Const[Int]]
x: PartialFunction[Any,Const[Int]] = <function1>
scala> x = x orElse { case Const(y) => 3 }
x: PartialFunction[Any,Const[Int]] = <function1>
Ah ok, that may work then.
@dkoeplin More complete gist: http://scalakata.com/gist/4426add4aa90ecc0bbda0cf414e3cb2b Thoughts ?
Makes sense. Not 100% sure which parts are expected to be generated by the macro here though
Everything below:
@rewrite def bools_and(x:Bool, y:Bool): Bool
=====>
var bools_and_pf = PartialFunction.empty[(Bool, Bool), SrcCtx => Bool]
implicit def eatSC(b: Bool): SrcCtx => Bool = (x:SrcCtx) => b
def bools_and(x:Bool, y: Bool)(ctx: SrcCtx): Bool = {
val f: SrcCtx => Bool = (bools_and_pf.lift((x,y)).getOrElse(BoolAnd(x,y)))
f(ctx)
}
def rewrite_bools_and(pf: PartialFunction[(Bool, Bool), SrcCtx => Bool]) =
bools_and_pf = bools_and_pf orElse pf
@rewrite def bools_and(x:Bool, y:Bool): Bool = { case (BoolConst(x), BoolConst(y)) => BoolConst(x && y)}
=====>
same as before +
rewrite_bools_and( { case (BoolConst(x), BoolConst(y)) => BoolConst(x && y)} )
Seems like we need two macros then, a "rewritable" default declaration and a "rewrite" rule declaration.
e.g.
implicit def addSC(b: Exp[_]): SrcCtx => Exp[_] = (x: SrcCtx) => b
@rewritable def xxx(args...): R = stage(And(x,y))
====>
var xxx_pf = PartialFunction.empty[(args...), SrcCtx => R]
def xxx(args...)(implicit ctx: SrcCtx): R = {
val f: SrcCtx => R = (xxx_pf.lift((x,y)).getOrElse(stage(And(x,y)))
f(ctx)
}
def rewrite_xxx(pf: PartialFunction[(args...), SrcCtx => R]) =
xxx_pf = xxx_pf orElse pf
@rewrite def xxx(args...) = { case args => ... }
====>
rewrite_xxx({ case args => ... })
My two macros were your rewritable. The first macro was just meant to be "no default provided" I originally thought people should use rewrite_bools_and directly, but your @rewrite make sense too :+1:
Ohh I see, ok. Cool.
We rely on overloading and linearization order for rewrite rules Another solution is to use partial functions:
from
to