Type Inference
来源:互联网 发布:阿里云域名优惠码 编辑:程序博客网 时间:2024/04/30 04:40
1. Overview
Some functional programming languages, like Haskell, can infer almost all types, because they can perform global type inference. Scala can’t do this, in part because Scala has to support subtype polymorphism (inheritance), which makes type inference much harder.
2. When Explicit Type Annotations Are Required
In practical terms, you have to provide explicit type annotations for the following situations:
- A mutable
var
or immutableval
declaration where you don’t assign a value, (e.g.,abstract declarations in a class likeval book: String
,var count: Int
). - All method parameters (e.g.,
def deposit(amount: Money) = {…}
). - Method return types in the following cases:
(a) When you explicitly callreturn
in a method (even at the end).
(b) When a method is recursive.
(c) When two or more methods are overloaded (have the same name) and one of them calls another; the calling method needs a return type annotation.
(d) When the inferred return type would be more general than you intended, e.g.,Any
.(This case somewhat rare, fortunately.)
3. Case examples
(1) Case 3(c)
For case 3(c), we have the following example:
package com.brown/** * Created by BrownWong on 2016/9/29. */object StringUtilV1 { def joiner(strings: String*): String = strings.mkString(" ") def joiner(strings: List[String]): String = joiner(strings :_*)}object Hello { def main(args: Array[String]): Unit = { println(StringUtilV1.joiner("S", "C", "A", "L", "A")) println(StringUtilV1.joiner(List("S", "C", "A", "L", "A"))) }}
output
S C A L AS C A L A
Explanation
- Because the second
joiner
method calls the first, it requires an explicitString
return type. - Scala supports methods that take variable argument lists (sometimes just called variadic methods). The first
joiner
method has this signature:
def joiner(strings: String*): String = strings.mkString(" ")
The*
after theString
in the argument list says “zero or more Strings.” - The second
joiner
uses a special syntax to tell the compiler to convert the inputList
into the variable argument list expected by the firstjoiner
method:
def joiner(strings: List[String]): String = joiner(strings :_*)strings :_*
is to think of it as a hint to the compiler that you want the list strings to be treated as a variable argument list (*
) of some unspecified.
(2) Case 3(d)
For case 3(d), we have the following example:
package com.brown/** * Created by BrownWong on 2016/9/29. */object Hello { def makeList(strings: String*) = { if (strings.isEmpty) List(0) else strings.toList } def main(args: Array[String]): Unit = { val list: List[String] = makeList() // Type mismatch }}
We intended for makeList
to return a List[String]
, but when strings.length
equals zero
, we return List(0)
.
Instead, we should return List.empty[String]
or the special “marker” type for empty lists, Nil
.
When the if
clause returns List[Int]
and the else
clause returns List[String]
(the result of strings.toList
), the only possible inferred return type for the method is the closest common supertype of List[Int]
and List[String]
, which is List[Any]
.
Return type is added :
package com.brown/** * Created by BrownWong on 2016/9/29. */object Hello { def makeList(strings: String*): List[String] = { if (strings.isEmpty) Nil else strings.toList } def main(args: Array[String]): Unit = { val list: List[String] = makeList("Brown", "Wong") println(list) }}
output
List(Brown, Wong)
4. Common typing mistake and experience
(1) Experience
When developing APIs that are built separately from their clients, declare method return types explicitly and use the most general return type you can. This is especially important when APIs declare abstract methods.
(2) Common typing mistake
Let’s see the following example:
scala> def double(i: Int) { 2 * i }double: (i: Int)Unitscala> println(double(2))()
Why did the second command print ()
instead of 4
? Look carefully at what the scala interpreter said about the method signature: double (Int)Unit
. We thought we defined a method named double
that takes an Int
argument and returns a new Int
with the value “doubled,” but it actually returns Unit
. Why?
The cause of this unexpected behavior is a missing equals sign in the method definition.Here is the definition we actually intended:
scala> def double(i: Int) = { 2 * i }double: (i: Int)Intscala> println(double(2))4
There is a reason for this behavior.
Scala regards a method with the equals sign before the body as a function definition and a function always returns a value in functional programming.
On the other hand, when Scala sees a method body without the leading equals sign, it assumes the programmer intended the method to be a “procedure” definition, meant for performing side effects only with the return value Unit
. In practice, it is more likely that the programmer simply forgot to insert the equals sign!
Note
This behavior is too subtle and the mistake is easy to make. Because it is easy enough to define a function that returns Unit
, the “procedure” syntax is now deprecated as of Scala 2.11. Don’t use it!
5. Unit
type
We said before that Unit
behaves like void
in other languages. However, unlike void
, Unit
is actually a type with a single value, named ()
, which is a historical convention in functional languages.
Ref
《Programming Scala》
- Type Inference
- CSharp - Type Inference and usage of var
- Type Inference vs. Static/Dynamic Typing
- Swift:Minimizing Annotation with Type Inference
- 隐含类型局部变量(Local Variable Type Inference)
- C#3.0 隐含类型局部变量(Local Variable Type Inference)
- [WebKit] JavaScriptCore解析--高级篇(二) 类型推导(Type Inference)
- The Java™ Tutorials — Generics :Type Inference 类型推断
- Improved Type Inference in C++11: auto, decltype, and the new function declaration syntax
- 机器码下的多态类型推导-Polymorphic type inference for machine code
- kotlin findviewbyid报错: type inference failed: Not enough information to infer parameter T in...
- C++11/C++14 (三)TYPE INFERENCE (AUTO) AND RANGE-BASED FOR LOOP
- variational inference
- Bayesian Inference
- Variational Inference
- LDA-inference
- Jetson-inference
- 使用 XSD Inference 实用程序
- NMF分解
- CSS WILL-CHANGE 属性
- yii2大型商城实战开发(后台登陆功能)
- 【深度学习:目标检测】RCNN学习笔记(4):fast rcnn
- div浮动层,遮罩层屏幕居中(水平垂直居中)CSS代码
- Type Inference
- C语言学习——第一篇博客 (二)
- poj 3694 Network
- HDU#1040:As Easy As A+B
- 一个DIV做的LODING动画CSS3动画
- 剑指offer:数组中只出现一次的数字(java)
- 【深度学习:目标检测】RCNN学习笔记(5):faster rcnn
- 自定义控件之仿优酷菜单
- TCP拥塞控制算法内核实现剖析