什么是'A类[_]`有用吗?

时间:2012-10-08 09:34:45

标签: scala higher-kinded-types

符号class A[_]def a[_](x: Any)的类型具有无法在正文中引用的类型参数,因此我看不到它在哪里有用以及它为什么编译。如果尝试引用此类型参数,则会引发错误:

scala> class A[_] { type X = _ }
<console>:1: error: unbound wildcard type
       class A[_] { type X = _ }
                             ^

scala> def a[_](x: Any) { type X = _ }
<console>:1: error: unbound wildcard type
       def a[_](x: Any) { type X = _ }
                                   ^

有人可以告诉我这种类型在Scala中是否有用例?确切地说,我并不是指类型参数中的存在类型或更高级的类型,只是那些构成完整类型参数列表的litte [_]

4 个答案:

答案 0 :(得分:4)

我对这里的含义有一些随意的想法:

https://issues.scala-lang.org/browse/SI-5606

除了琐碎的用例之外,要求编译器组成一个名字,因为我真的不在乎(虽然我可能会在以后实现该类时将其命名),但这个仍然让我觉得有用:

  

另一个用例是不推荐使用类型参数,因为   类型推断的改进使其变得多余。

trait T[@deprecated("I'm free","2.11") _, B <: S[_]] 
  

然后,假设,   可以警告使用T[X, Y]但不使用T[_, Y]

虽然注释是否会出现(值参数样式)或之后(样式样式上的注释)并不明显。

[编辑:“为什么要编译”:case class Foo[_](i: Int)仍然在2.9.2上很好地崩溃]

答案 1 :(得分:4)

因为我没有得到我期望的答案,所以我把它带到scala-language

我在这里粘贴了Lars Hupel的答案(因此,所有学分都适用于他),这主要解释了我想知道的内容:

  

我要在这里试一试。我认为该功能的使用得到了   在谈论类型成员时要明确。

     

假设您必须实现以下特征:

trait Function {
  type Out[In]
  def apply[In](x: In): Out[In]
}
     

这将是一个(通用)函数,其中返回类型依赖于   输入类型。实例的一个例子:

val someify = new Function {
  type Out[In] = Option[In]   def
  apply[In](x: In) = Some(x)
}

someify(3) res0: Some[Int] = Some(3)
     

到目前为止,这么好。现在,您将如何定义常量函数?

val const0 = new Function {
  type Out[In] = Int
  def apply[In](x: In) = 0
}

const0(3) res1: const0.Out[Int] = 0
     

(类型const0.Out[Int]相当于Int,但事实并非如此   这样打印。)

     

请注意实际上未使用类型参数In。那么,这是怎么回事   你可以用_编写它:

val const0 = new Function {
  type Out[_] = Int
  def apply[In](x: In) = 0
}
     

在这种情况下,将_视为类型参数的名称   实际上不能被提及。这是一个类型的功能   不关心参数的级别,就像值一样   电平:

(_: Int) => 3 res4: Int => Int = <function1>
     

除了......

type Foo[_, _] = Int
<console>:7: error: _ is already defined as type _
       type Foo[_, _] = Int
     

将其与:

进行比较
(_: Int, _: String) => 3 res6: (Int, String) => Int = <function2>
     

所以,最后:

type F[_] = ConstType // when you have to implement a type member def
foo[_](...) // when you have to implement a generic method but don't
            // actually refer to the type parameter (occurs very rarely)
     

您提到的主要内容class A[_]与...完全对称   除了没有真正的用例外。

     

考虑一下:

trait FlyingDog[F[_]] { def swoosh[A, B](f: A => B, a: F[A]): F[B] }
     

现在假设您要为您的普通版创建FlyingDog的实例   旧class A

new FlyingDog[A] { ... }
// error: A takes no type parameters, expected: one
// (aka 'kind mismatch')
     

有两种解决方案:

     
      
  1. 改为声明class A[_]。 (不要这样做。)

  2.   
  3. 使用类型lambda:

    new FlyingDog[({ type λ[α] = A })#λ]
    
  4.         

    甚至

    new FlyingDog[({ type λ[_] = A })#λ]
    

答案 2 :(得分:2)

Scala中的下划线表示存在类型,即未知类型参数,有两个主要用法:

  • 用于不关心类型参数的方法
  • 它用于表示一个类型参数是类型构造函数的方法。

类型构造函数基本上需要一个类型参数来构造具体类型。例如,您可以采用以下签名。

def strangeStuff[CC[_], B, A](b:B, f: B=>A): CC[A] 

这是一个函数,对于某些CC[_],例如List[_],从B和函数List[A]开始创建B=>A

为什么这会有用?事实证明,如果你将这种机制与implicits和类型类一起使用,你可以通过编译器推理获得所谓的ad-hoc多态性。

想象一下,例如,您有一些更高级的类型:Container[_]具有具体实施的层次结构:BeautifulContainer[_]BigContainer[_]SmallContainer[_]。要构建容器,您需要

trait ContainerBuilder[A[_]<:Container[_],B] { 

def build(b:B):A[B]

}

所以基本上ContainerBuilder就是特定类型的容器A [_]可以使用B构建A [B]。

虽然这会有用吗?那么你可以想象你可能在其他地方定义了一个函数,如下所示:

def myMethod(b:B)(implicit containerBuilder:ContainerBuilder[A[_],B]):A[B] = containerBuilder.build(b)

然后在你的代码中你可能会这样做:

val b = new B()
val bigContainer:BigContainer[B] = myMethod(b)
val beautifulContainer:BeautifulContainer[B] = myMethod(b)

实际上,编译器将使用myMethod所需的返回类型来查找满足所需类型约束的隐式,如果没有ContainerBuilder可以隐含地满足所需的约束,则会抛出编译错误。

答案 3 :(得分:1)

当你处理参数化类型的实例而不关心类型参数时,这很有用。

trait Something[A] {
  def stringify: String
}

class Foo extends Something[Bar] {
  def stringify = "hop"
}

object App {
  def useSomething(thing: Something[_]) :String = {
    thing.stringify
  }
}