Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
隐式使用Scala实现类型相等_Scala_Types_Implicit - Fatal编程技术网

隐式使用Scala实现类型相等

隐式使用Scala实现类型相等,scala,types,implicit,Scala,Types,Implicit,我一直在读一些关于Scala类型级编程的东西。主要是Apocalisp博客,还有Alexander Lehmann在youtube上的演讲 我有点被困在一些我想可能是非常基本的东西上,即使用隐式比较两种类型,如下所示: implicitly[Int =:= Int] Apocalisp博客上的马克说: 这对于捕获范围内且类型为T的隐式值非常有用 我知道如何使这项工作,但我真的不知道为什么它的工作,所以不想继续 在上面的例子中,作用域中是否有一个类型为“Int”的隐式函数,它“隐式地”从以太中提

我一直在读一些关于Scala类型级编程的东西。主要是Apocalisp博客,还有Alexander Lehmann在youtube上的演讲

我有点被困在一些我想可能是非常基本的东西上,即使用隐式比较两种类型,如下所示:

implicitly[Int =:= Int]
Apocalisp博客上的马克说:

这对于捕获范围内且类型为T的隐式值非常有用

我知道如何使这项工作,但我真的不知道为什么它的工作,所以不想继续

在上面的例子中,作用域中是否有一个类型为“Int”的隐式函数,它“隐式地”从以太中提取,允许代码编译?这与的“function1”返回类型有什么关系

res0: =:=[Int,Int] = <function1>
编译?在这种情况下,“Foo”隐式从何而来


如果这是一个非常愚蠢的问题,请提前道歉,并感谢您的帮助

X=:=Y
只是类型
=:=[X,Y]
的语法糖(中缀符号)

因此,当您隐式地执行
[Y=:=Y]
时,您只需查找类型为
=:=[X,Y]
的隐式值。
=:=
是在
Predef
中定义的一般特征

另外,
=:=
是有效的类型名,因为类型名(就像任何标识符一样)可以包含特殊字符

从现在起,让我们将
=:=
重命名为
IsSameType
,并删除中缀符号,以使代码看起来更简单、更不神奇。 这就隐式地为我们提供了
[IsSameType[X,Y]]

以下是如何定义此类型的简化版本:

sealed abstract class IsSameType[X, Y]
object IsSameType {
   implicit def tpEquals[A] = new IsSameType[A, A]{}
}
注意
tpEquals
如何为任何类型
A
提供
IsSameType[A,A]
的隐式值。 换句话说,当且仅当
X
Y
是同一类型时,它提供
IsSameType[X,Y]
的隐式值。 因此
隐式[IsSameType[Foo,Foo]]
编译得很好。 但是
隐式[IsSameType[Int,String]]
没有,因为类型
IsSameType[Int,String]
的作用域中没有隐式,因为这里不适用
tpEquals

因此,通过这个非常简单的构造,我们能够静态地检查某个类型
X
是否与另一个类型
Y
相同


现在,这里有一个例子说明它是如何有用的。假设我想定义一个对类型(忽略它已经存在于标准库中的事实):

Pair
通过其2个元素的类型进行参数化,这2个元素可以是任何元素,最重要的是它们之间不相关。 现在,如果我想定义一个方法
toList
,将对转换为2元素列表,该怎么办? 这种方法只有在
X
Y
相同的情况下才有意义,否则我将被迫返回
列表[Any]
。 我当然不想把
Pair
的定义改为
Pair[t](x:t,y:t)
,因为我真的想拥有异构类型的对。 毕竟,只有在调用
toList
时,我才需要X==Y。所有其他方法(如
swap
)都应该可以在任何类型的异构对上调用。 因此,最后我真的希望静态地确保X==Y,但仅当调用
toList
时,在这种情况下,返回一个
List[X]
(或一个
List[Y]
,这是一样的):

但是,在实际实现
toList
时,仍然存在一个严重的问题。如果我尝试编写明显的实现,则无法编译:

def toList( implicit evidence: IsSameType[X, Y] ): List[Y] = List[Y]( x, y )
编译器会抱怨
x
不是
Y
类型。实际上,就编译器而言,
X
Y
仍然是不同的类型。 只有通过仔细构造,我们才能静态地确定X==Y (即,
toList
接受类型为
IsSameType[X,Y]
的隐式值,并且只有当X==Y时,它们才由方法
tpEquals
提供)。 但是编译器肯定不会破译这个巧妙的结构来得出X==Y的结论

要解决这种情况,我们可以做的是提供从X到Y的隐式转换,前提是我们知道X==Y(或者换句话说,我们在范围中有一个
IsSameType[X,Y]

现在,我们对
toList
的实现最终编译良好:
x
将通过隐式转换
sameTypeConvert
简单地转换为
Y

作为最后一个调整,我们可以进一步简化:假设我们已经将隐式值(
证据
)作为参数, 为什么不让这个值实现转换呢?像这样:

sealed abstract class IsSameType[X, Y] extends (X => Y) {
  def apply( x: X ): Y = x.asInstanceOf[Y]
}
object IsSameType {
   implicit def tpEquals[A] = new IsSameType[A, A]{}
}    
然后我们可以删除方法
sameTypeConvert
,因为隐式转换现在由
IsSameType
实例本身提供。 现在,
IsSameType
有双重目的:静态地确保X==Y,并且(如果是)提供隐式转换,这实际上允许我们将
X
的实例视为
Y
的实例

我们现在基本上重新实现了
=:=
中定义的类型
Predef


更新:从评论中可以看出,使用
替代
似乎让人们感到困扰(尽管它实际上只是一个实现细节,而且
IsSameType
的任何用户都不需要进行转换)。事实证明,即使在实现过程中也很容易摆脱它。瞧:

sealed abstract class IsSameType[X, Y] extends (X => Y) {
  def apply(x: X): Y
}
object IsSameType {
  implicit def tpEquals[A] = new IsSameType[A, A]{
    def apply(x: A): A = x
  }
}
基本上,我们只需保留
apply
抽象,只在
tpEquals
中正确实现它,在这里我们(和编译器)知道传递的参数和返回值实际上具有相同的类型。因此,不需要任何铸造。真的是这样

请注意,最后,相同的演员阵容
def toList( implicit evidence: IsSameType[X, Y] ): List[Y] = List[Y]( x, y )
// A simple cast will do, given that we statically know that X == Y
implicit def sameTypeConvert[X,Y]( x: X )( implicit evidence: IsSameType[X, Y] ): Y = x.asInstanceOf[Y]
sealed abstract class IsSameType[X, Y] extends (X => Y) {
  def apply( x: X ): Y = x.asInstanceOf[Y]
}
object IsSameType {
   implicit def tpEquals[A] = new IsSameType[A, A]{}
}    
sealed abstract class IsSameType[X, Y] extends (X => Y) {
  def apply(x: X): Y
}
object IsSameType {
  implicit def tpEquals[A] = new IsSameType[A, A]{
    def apply(x: A): A = x
  }
}