[英]Getting around invariant result type in State
I would like to define a State
that builds a concrete subtype of a trait, as per decodeFoo
: 我想定义一个
State
是建立一个特质的具体亚型,按decodeFoo
:
sealed trait Foo
case class Bar(s: String) extends Foo
case class Baz(i: Int) extends Foo
val int: State[Seq[Byte], Int] = State[Seq[Byte], Int] {
case bs if bs.length >= 4 =>
bs.drop(4) -> ByteBuffer.wrap(bs.take(4).toArray).getInt
case _ => sys.error(s"Insufficient data remains to parse int")
}
def bytes(len: Int): State[Seq[Byte], Seq[Byte]] = State[Seq[Byte], Seq[Byte]] {
case bs if bs.length >= len => bs.drop(len) -> bs.take(len)
case _ => sys.error(s"Insufficient data remains to parse $len bytes")
}
val bytes: State[Seq[Byte], Seq[Byte]] = for {
len <- int
bs <- bytes(len)
} yield bs
val string: State[Seq[Byte], String] = bytes.map(_.toArray).map(new String(_, Charset.forName("UTF-8")))
val decodeBar: State[Seq[Byte], Bar] = string.map(Bar)
val decodeBaz: State[Seq[Byte], Baz] = int.map(Baz)
val decodeFoo: State[Seq[Byte], Foo] = int.flatMap {
case 0 => decodeBar
case 1 => decodeBaz
}
This will not compile as State
is defined in cats as type State[S, A]
and the compiler responds: 这不会编译,因为
State
在cat中定义为type State[S, A]
并且编译器响应:
Error:(36, 15) type mismatch;
found : cats.data.State[Seq[Byte],FooBarBaz.this.Bar]
(which expands to) cats.data.IndexedStateT[cats.Eval,Seq[Byte],Seq[Byte],FooBarBaz.this.Bar]
required: cats.data.IndexedStateT[cats.Eval,Seq[Byte],Seq[Byte],FooBarBaz.this.Foo]
Note: FooBarBaz.this.Bar <: FooBarBaz.this.Foo, but class IndexedStateT is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)
case 0 => decodeBar
I can work around this by widening the definitions of decodeBar
& decodeBaz
to be of type State[Seq[Byte], Foo]
. 我可以通过将
decodeBar
和decodeBaz
的定义扩展为State[Seq[Byte], Foo]
类型来解决这个问题。 Is that the best way forward? 这是最好的前进方式吗? Or can I take a different approach that avoids widening these types?
或者我可以采取不同的方法来避免扩大这些类型?
Functor.widen Functor.widen
Functor.widen should do the trick. Functor.widen应该做的伎俩。 Full compilable example (with kind-projector):
完整的可编辑示例(使用kind-projector):
import cats.data.State
import cats.Functor
object FunctorWidenExample {
locally {
sealed trait A
case class B() extends A
val s: State[Unit, B] = State.pure(new B())
val t: State[Unit, A] = Functor[State[Unit, ?]].widen[B, A](s)
}
}
in your case, it would be something like: 在你的情况下,它将是这样的:
val decodeFoo: State[Seq[Byte], Foo] = int.flatMap {
case 0 => Functor[State[Seq[Byte], ?]].widen[Bar, Foo](decodeBar)
case 1 => Functor[State[Seq[Byte], ?]].widen[Bar, Foo](decodeBaz)
}
Other possible work-arounds 其他可能的解决方法
(not really necessary, just to demonstrate the syntax that might be less known): (不是真的有必要,只是为了演示可能不太知道的语法):
Explicit type ascriptions: 明确的类型归属:
val decodeFoo: State[Seq[Byte], Foo] = int.flatMap { case 0 => decodeBar.map(x => (x: Foo)) case 1 => decodeBaz.map(x => (x: Foo)) }
Using <:<
as method (those things actually do have a meaningful apply
): 使用
<:<
作为方法(那些事情实际上有一个有意义的apply
):
val decodeFoo: State[Seq[Byte], Foo] = int.flatMap { case 0 => decodeBar.map(implicitly: Bar <:< Foo) case 1 => decodeBaz.map(implicitly: Baz <:< Foo) }
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.