简体   繁体   English

Scala泛型:专门研究方法

[英]Scala generics: specializing a method

In Haskell I can define a generic type for a tree as: 在Haskell中,我可以为树定义泛型类型:

type Tree t = Leaf t | Node (Tree t) (Tree t)

If I want to define a function for a specific parameterization of Tree, I can simply do: 如果我想为Tree的特定参数化定义一个函数,我可以简单地做:

-- Signature of a function that takes a tree of bool
foo :: Tree Bool -> Int

-- Takes a tree of numbers
bar :: (Num n) => Tree n -> Bool

We can define a similar tree type in Scala with: 我们可以在Scala中定义一个类似的树类型:

abstract class Tree[T]()
case class Leaf[T](t: T) extends Tree[T]
case class Node[T](left: Tree[T], right: Tree[T]) extends Tree[T]

But how can I define a method for Tree that only applies to certain types? 但是,如何为Tree定义仅适用于某些类型的方法? Do I need to use inheritance or there's a way to say: 我需要使用继承还是有办法说:

abstract class Tree[T]() {
  // Method only for Tree[String]:
  def foo[String] = ...
}

In Haskell types don't have instance methods like Scala does. 在Haskell类型中没有像Scala那样的实例方法。

foo in your example should be defined (preferably) in Tree 's companion object. 你的例子中的foo应该在Tree的伴随对象中定义(最好)。

sealed abstract class Tree[T]()
case class Leaf[T](t: T) extends Tree[T]
case class Node[T](left: Tree[T], right: Tree[T]) extends Tree[T]

object Tree {
  // Method only for Tree[String]:
  def foo(tree: Tree[String]) = ...
}

PS: IMO a sealed class or trait is more appropriate here. PS:IMO sealed类或特性在这里更合适。 ( Scala's sealed abstract vs abstract class ) Scala的密封抽象与抽象类

PS II: I am just typing Gregor Raýman's comment as an answer. PS II:我只是输入GregorRaýman的评论作为答案。

This may not be the answer you are looking for, as I haven't done much Haskell, but it's a possibility: You can define a trait that can only be mixed into specific instances of a tree: 这可能不是你要找的答案,因为我没有做过太多的Haskell,但它是一个可能性:你可以定义一个trait ,只能混合成树的具体情况:

trait StringFooFunctionality {
   this: Tree[String] => // Selftype, can only be mixed in to classes that are Tree[String]
   def foo = "Yay" // String is the datatype of Tree here
}

You would use this like this: 你可以这样使用:

val sNode = new Node(Leaf("a"), Leaf("b")) with StringFooFunctionality
sNode.foo
// Yay

The downside is that it explicitly needs to be mixed in on object creation. 缺点是明确需要在对象创建中混合使用。

Other possibility is to create a new trait called StringTree : 其他可能性是创建一个名为StringTree的新特征:

trait StringTree extends Tree[String] {
   def foo = ...
}

But you would have to define the other String datatypes: 但您必须定义其他String数据类型:

case class StringLeaf(t: String) extends StringTree
case class StringNode(left: StringTree, right: StringTree) extends StringTree

And when you encounter a Tree[T] you can pattern match on it to see if it's StringTree . 当你遇到Tree[T]你可以在它上进行模式匹配,看看它是否是StringTree

The obvious way (and equivalent to Haskell) is to define a method which takes Tree[String] as an argument, as in muhuk's answer. 显而易见的方法(相当于Haskell)是定义一个将Tree[String]作为参数的方法,就像muhuk的答案一样。 If you want it to look like a method on Tree[String] , you can use implicit class: 如果你希望它看起来像Tree[String]上的方法,你可以使用隐式类:

implicit class Foo(val tree: Tree[String]) {
  def foo = ...
}

val tree: Tree[String] = ...

tree.foo // Foo needs to be in scope here

I recommend avoiding Akos Krivachy's answer in most circumstances. 我建议在大多数情况下避免使用Akos Krivachy的答案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM