2010-06-18 93 views
8

我在斯卡拉有一个特点,有一个单一的方法。将其称为Computable,单个方法是compute(input:Int):Int。我不知道我是否应该Scala中的特征,函数N或特征继承函数N?

  • 把它作为一个单独的方法独立的特点。
  • 从(Int => Int)继承并将“compute”重命名为“apply”。
  • 只需摆脱Computable并使用(Int => Int)。

赞成它是一个特质的一个因素是,我可以有用地添加一些额外的方法。但是,当然如果它们都是用计算方法实现的,那么我可以将它们分解成单独的对象。

有利于使用函数类型的一个因素是简单性,以及匿名函数的语法比匿名的Computable实例更简洁的事实。但是之后我无法区分实际上是Computable实例的对象,这些对象是将Int映射到Int的其他函数,但并不意味着它们与Computable在同一个上下文中使用。

其他人如何解决这类问题?这里没有对或错的答案;我只是在寻找建议。

回答

3

创建一个特点是从一个函数类型扩展可以是有用的出于几个原因。

  1. 你的函数对象做一些特别的东西和非显而易见的(并且很难输入),你可以在参数构造函数的细微变化。例如,假设您正在编写一个特征以在XML树上执行XPath查询。 apply函数会隐藏构建XPath查询机制时的几种工作,但仍然值得实现接口,以便您可以从大量不同节点开始使用mapflatMap进行查询。

  2. 作为#1的扩展,您希望在构建时进行一些处理(例如,解析XPath表达式并编译为快速运行),您可以提前一次在对象的构造函数中执行一次如果你只是咖喱Function•不用子类,编译只可能发生在运行时,所以它会被重复,每次查询。)

  3. 要传递的加密功能(一种Function1[String,String])作为隐式的,但并非所有的Function1[String,String]都执行加密。通过从Function1[String,String]派生并命名子类/特征EncryptionFunction,可以确保只有右侧子类的函数才会隐式传递。 (声明Type EncryptionFunction = String => String时不是这样。)

我希望那是明确的。

1

这听起来像你可能想要使用structural type。它们也被称为隐式接口。

然后,您可以重构当前接受Computable以接受任何具有compute(input: Int)方法的方法。

+0

好的,这样可以使任何具有计算方法的对象可用作Computable。但是我更感兴趣知道的是,对于使Computable成为函数类型还是反对,有哪些考虑? 我注意到的一个问题是,如果Computable是一个函数类型,那么实现它的类不能是任何其他函数类型。如果我有另一个继承自(String => String)的接口,Displayable,那么一个类不能同时为Computable和Displayable,因为它会从两个特征继承Function1,但具有不同的类型参数。 – 2010-06-18 10:54:13

+1

没错,这是反对扩展(或直接使用)Int => Int的一个论据。如果您期望这将是典型的用例,那么自定义名称可能会更好。 – 2010-06-18 11:00:51

1

一个选项是定义一个类型(你仍然可以称之为Computable),它现在是Int => Int。每当你需要可计算的东西时使用它。您将获得从Function1继承的所有好处。然后如果你意识到你需要更多的方法,你可以改变类型到另一个特征。

起初:

type Computable = Int => Int 

后来:它

type Computable = ComputableTrait // with its own methods. 

一个缺点是,你所定义的类型是不是一个真正的新的类型,更多的是一种别名。因此,直到您将其更改为特性为止,编译器仍然会接受其他Int => Int函数。至少,你(开发者)可以区分。当你改变一个特性(并且差异变得重要)时,编译器会发现你什么时候需要一个Computable,但是Int => Int。

如果你希望编译器从第一天开始拒绝其他Int => Int -s,那么我建议使用trait,但是扩展Int => Int。当你需要调用它时,你仍然会有更方便的语法。

另一种选择可能是让一个trait和伴随对象具有接受Int => Int的apply方法并创建一个Computable。然后创建新的Computables就像编写简单的匿名函数一样简单,但是您仍然可以进行类型检查(您会因隐式转换而失去它)。此外,你可以毫无问题地混合在一起(但是随后的伴侣对象的应用不能照原样使用)。

8

如果你让一个特质,仍然希望能够使用轻量级的函数语法,你也可以附加您想要将它们添加在地方的隐式转换:

scala> trait Computable extends (Int => Int) 
defined trait Computable 

scala> def computes(c: Computable) = c(5) 
computes: (c: Computable)Int 

scala> implicit def toComputable(f: Int => Int) = new Computable { def apply(i: Int) = f(i) } 
toComputable: (f: (Int) => Int)java.lang.Object with Computable 

scala> computes((i: Int) => i * 2) 
res0: Int = 10 
+2

我正在使用非常类似的方法。我有'曲线扩展(Double => Double)'和'表面扩展((Double,Double)=> Double)'特性,提示正常的Scala函数。 如果您在特质“Computable”的伴随对象中定义了转换,当从任何类型“T”搜索到“Computable”的视图时,它会自动处于隐式范围内。 – retronym 2010-06-18 13:37:36

+0

这实际上就是我所做的。但是我想知道可计算性状实际上“买”了什么。如果我总是使用它(Int => Int),那么为什么要麻烦呢?从我可以告诉它只有纪录片的好处。 – 2010-06-18 17:15:32

+1

如果只是为了文档,你可以定义一个类型别名:'类型Computable = Int => Int' .. – 2010-06-19 16:26:10