假设我正在尝试“抽象执行”:
import scala.language.higherKinds
class Operator[W[_]]( f : Int => W[Int] ) {
def operate( i : Int ) : W[Int] = f(i)
}
现在我可以定义Operator[Future]
或Operator[Task]
等。例如...
import scala.concurrent.{ExecutionContext,Future}
def futureSquared( i : Int ) = Future( i * i )( ExecutionContext.global )
REPL风格...
scala> val fop = new Operator( futureSquared )
fop: Operator[scala.concurrent.Future] = Operator@105c54cb
scala> fop.operate(4)
res0: scala.concurrent.Future[Int] = Future(<not completed>)
scala> res0
res1: scala.concurrent.Future[Int] = Future(Success(16))
万岁!
但是我也可能想要一个简单的同步版本,所以我在某个地方定义
type Identity[T] = T
我可以定义一个同步运算符...
scala> def square( i : Int ) : Identity[Int] = i * i
square: (i: Int)Identity[Int]
scala> val sop = new Operator( square )
sop: Operator[Identity] = Operator@18f2960b
scala> sop.operate(9)
res2: Identity[Int] = 81
甜。
但是,结果的推断类型是Identity[Int]
,而不是更简单直接的Int
,这很尴尬。当然,这两种类型实际上是相同的,因此在各个方面都相同。但是我希望我的图书馆的客户对这些抽象过度执行的东西一无所知,不要混淆。
我可以手工写一个包装纸...
class SimpleOperator( inner : Operator[Identity] ) extends Operator[Identity]( inner.operate ) {
override def operate( i : Int ) : Int = super.operate(i)
}
那行得通...
scala> val simple = new SimpleOperator( sop )
simple: SimpleOperator = SimpleOperator@345c744e
scala> simple.operate(7)
res3: Int = 49
但是,这感觉非常笨拙,特别是如果我的抽象执行类具有很多方法而不仅仅是一个方法的话。而且我必须记住,随着通用类的发展,使包装器保持同步。
是否有一些更通用,更可维护的方式来获取Operator[Identity]
版本,从而使包含类型从类型推断和API文档中消失?
答案 0 :(得分:3)
更多的是长评论而不是答案...
但是,结果的推断类型是Identity [Int],而不是更简单直接的Int,这很尴尬。当然,这两种类型的表观类型实际上是相同的,因此在各个方面都相同。但是我希望我的图书馆的客户对这些抽象过度执行的东西一无所知,不要混淆。
这听起来像您想将Indentity[T]
转换回T
...您是否考虑过输入类型?
scala>def f[T](t: T): Identity[T] = t
scala>f(3)
// res11: Identity[Int] = 3
scala>f(3): Int
// res12: Int = 3
// So in your case
scala>sop.operate(9): Int
// res18: Int = 81
答案 1 :(得分:2)
正如史蒂夫·沃尔德曼(Steve Waldman)在给type Identity[T] = T
的评论中所建议的那样,类型T
和Identity[T]
实际上是相同的,没有任何仪式,在呼叫站点或其他任何地方都可以替换且透明。例如,立即使用以下方法即可正常运行
sop.operate(9) // res2: cats.Id[Int] = 81
def foo(i: Int) = i
foo(sop.operate(9)) // res3: Int = 81
Cats的 extract
是pure
的对偶并从其上下文中提取值,因此也许我们可以为不熟悉上述等效项的用户提供类似的方法(如果您自己,参见我以前的修改)。
答案 2 :(得分:2)
可以通过显式提供类型来完成,但是对于外部用户调查方法签名仍然看起来很神奇。
type Identity[T] = T
def square( i : Int ):Int = i * i
class Operator[W[_], T <: W[Int] ]( f : Int => T ) {
def operate(i : Int):T = f(i)
}
val op = new Operator[Identity,Int](square)
op.operate(5)
//res0: Int = 25
同样适用于new Operator[Future,Future[Int]]
。