[英]Scala supertype of call-by-name and call-by-value
我想对下面的代码进行类型检查。
val f: Int => String = x => "a"
val g: (=> Int) => String = x => "b"
def t(h: ???): String = h(42)
t(f)
t(g)
应该在“ ???”中添加什么? 我已阅读此链接并尝试过。
t[Int => String, (=> Int) => String].common
res4s: reflect.runtime.universe.TypeTag[=> Int with Int => String] = TypeTag[=> Int with Int => String]
因此,我在???中放置了“ => Int with Int => String”,但是t(g)不会进行类型检查。 我已经尝试过“((=> Int with Int)=>字符串)”,但是t(f)没有进行类型检查。
所以问题是
“ => Int with Int => String”的含义是什么,为什么t(g)不进行类型检查
“(=> Int with Int)=>字符串”的含义是什么,为什么t(f)不进行类型检查
应该放什么???
非常感谢。
首先,类型X => Y
即Function1[X,Y]
在类型参数X
上是互变的,因此您应该搜索X
最小公称子类型,而不是X
最大公称父类型,并且=>X
第二-不幸的是没有这种亚型。 而且,除使用type =>X
函数外,很难定义任何东西。 尽管已知此类定义稍后会在类型检查级别上对Function0[X]
消减,但类型=>X
和()=>X
并不等效。
幸运的是,我们可以使用类型类为我们的需求定义类型之间的复杂关系。
因为我们只能在参数类型中使用=>X
,所以我们可以定义这样的事情:
case class ParamConvertible[X, Y, T](apply: ((Y => T) => X => T))
看起来和工作起来像收缩函子的某些狭窄版本
并提供两个明显的实现
implicit def idParamConvertible[X, T] = ParamConvertible((f: X => T) => (x: X) => f(x))
implicit def byNameParamConvertible[X, T] = ParamConvertible((f: (=> X) => T) => (x: X) => f(x))
现在我们可以将您的t
函数推广为
def t[T](h: T => String)
(implicit conv: ParamConvertible[Int, T, String]): String =
conv.apply(h)(42)
此时,您的
t(f)
t(g)
应该编译并运行良好
问题在于Int => String
和(=> Int) => String
没有有用的通用超级类型。 Int => String
是按值调用函数的类型,该函数将Int
值作为第一个参数并返回String
。
与此相反, (=> Int) => String
是一个按名称调用函数,该函数将Int
类型的表达式作为第一个参数。 每次您访问call-by-name参数时,都会对表达式进行求值。 因此,它实际上是一个返回Int
的零参数函数。
您可以执行的操作是将“按名称调用”功能转换为“按值调用”功能,以便t(h: Int => String): String
类型也使用g
进行检查。 您只需调用t(g(_))
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.