鉴于这些课程:
class Step
case class Publish(p: String) extends Step
case class Receive(p: String) extends Step
class Data
case class Input(p: Int) extends Data
case class Output(p: Int) extends Data
type P = Publish
type R = Receive
type I = Input
type O = Output
我希望method
/ trait
/ class
在Step*
/ Publish
序列中获取Receive
个参数并返回一种在Data
/ Input
序列中获取Output
个参数的方法。
例如,对于:
val flow = Flow {"flow" =>
(P("1"), R("2"), P("3"), R("4"), R("5"), P("6"), R("7"))
}
我应该可以接听电话:
flow(I(1), O(2), I(3), O(4), O(5), I(6), O(7))
我最接近的是将Flow
定义为对象:
object Flow {
def apply[T](f: String => T)(implicit val s: String): T => Unit = f(s) => ()
}
它为上面的例子提供了flow: (P, R, P, R, R, P, R) => Unit
如何映射P -> I
和R -> O
以便使用输入和输出对flow
进行调用,并确保在编译时进行类型检查?
在寻找解决方案时,我偶然发现了shapeless
和scalaz
作为我认为可能对我有用的框架,但我无法弄明白。
在this answer之后,我将Flow
改写为:
object Flow {
def apply[T <: HList, S <: HList](t: T)
(implicit mapper : Mapper[IOFlow.type, T]): S => Unit = {
val s = t.map(IOFlow)
s => ()
}
}
object IOFlow extends Poly1 {
implicit val in = at[P]{ i => makeI(i) }
implicit val out = at[R]{ r => makeO(r) }
def makeI(p: P) = Input // so the return type is Input.type
def makeO(p: R) = Output // and Output.type
}
object Main extends App {
val initialList = HList(P("1"), R("2"), P("3"), R("4"), R("5"), P("6"), R("7"))
val flow = Flow(initialList)
}
我收到implicit mapper
的错误,实际上是t.map(IOFLow)
收到的:
Error: could not find implicit value for parameter mapper: shapeless.ops.hlist.Mapper[com.tasegula.scala.shapeless.IOFlow.type,this.Repr]
val flow = Flow(hlist)
^
所以:
Flow#apply
方法声明更改为apply(t: Step*)
,因此调用将是Flow(P("1"), R("2"), P("3"), R("4"), R("5"), P("6"), R("7"))
?答案 0 :(得分:2)
你怎么能这么容易地做到这一点?我会看一下Shapeless,它可以通过定义Poly来实际完成你想做的一切:
object IOFlow extends Poly1{
implicit val in = at[P]{ i => makeI(i) }
implicit val out = at[R]{ r => makeO(r) }
def makeI(p: P): I
}
通过将您的调用转换为HList,您将能够映射:
val input: I :: O :: I :: O :: I :: O = prprpr.map(IOFlow)
答案 1 :(得分:0)
经过多次试验和错误,终于得到了解决方案:
为Data
类型创建了一个包装器,所以:
Publish returns a Wrapper1[Input]
Receive returns a Wrapper1[Output]
另外,因为我有一系列步骤,所以我为每个可能的参数创建了Wrapper
:
case class Wrapper1[T1](s1: String)
case class Wrapper2[T1, T2](s1: String, s2: String)
...
此包装器中的每一个都有一个方法,它将其与Wrapper1[S]
结合并返回Wrapper(n+1)[T1..Tn, S]
。
此外,每个都有一个start方法,它接受给定类型的n个参数。
case class Wrapper1[T1](s1: String) {
def +[S](step: W1[S]): W2[T1, S] = W2(s1, step.s1)
def start(p1: T1) = println("PARAMS: " + List(p1))
}
case class Wrapper2[T1, T2](s1: String, s2: String) {
def +[S](step: W1[S]): W3[T1, T2, S] = W2(s1, s2, step.s1)
def start(p1: T1, p2: T2) = println("PARAMS: " + List(p1, p2))
}
现在,IOFlow
成为每个apply
Wrapper
的对象:
object IOFlow {
def apply[T1](flow: W1[T1]): (T1) => Unit = flow.start
def apply[T1, T2](flow: W2[T1, T2]): (T1, T2) => Unit = flow.start
...
}
Main
app:
object Main extends App {
val flow = IOFLow(P("1") + R("2") + P("3") + R("4") + R("5") + P("6") + R("7"))
flow(I(1), O(2), I(3), O(4), O(5), I(6), O(7))
}
所以,在窗帘后面,会发生这种情况:
P("1") + R("2") + P("3") + R("4") + R("5") + P("6") + R("7") =>
W1[I] + W1[O] + W1[I] + W2[O] + W1[O] + W1[I] + W1[O]
然后每个+
采取行动:
W1[I] + W1[O] + W1[I] + W2[O] + W1[O] + W1[I] + W1[O] =
W2[I, O] + W1[I] + W2[O] + W1[O] + W1[I] + W1[O] =
W3[I, O, I] + W2[O] + W1[O] + W1[I] + W1[O] =
W4[I, O, I, O] + W1[O] + W1[I] + W1[O] =
W5[I, O, I, O, O] + W1[I] + W1[O] =
W6[I, O, I, O, O, I] + W1[O] =
W7[I, O, I, O, O, I, O]
所以电话实际上就是:
val flow = IOFlow(W7[I, O, I, O, O, I, O]("1", "2", "3", "4", "5", "6", "7"))
这意味着它将被调用:
apply[T1, T2, T3, T4, T5, T6, T7](flow: W7[T1, T2, T3, T4, T5, T6, T7]): (T1, T2, T3, T4, T5, T6, T7) => Unit = flow.start
这意味着flow
将是一个需要7个参数的函数:(T1, T2, T3, T4, T5, T6, T7)
并返回Unit
。此函数的行为在Wrapper7#start
方法中定义。