官术网_书友最值得收藏!

Refactoring the retirement calculator to use Option

Now that we know what Option can do for us, we are going to refactor one of the functions of the retirement calculator that we developed in Chapter 2Developing a Retirement Calculator, to improve the handling of some edge-case scenarios. If you have not done it yet, please follow the instructions at the beginning of the Chapter 2Developing a Retirement Calculator, to set up the project.

In RetCalc.scala, we are going to change the return type of nbMonthsSaving. In Chapter 2Developing a Retirement Calculator, we returned Int.MaxValue if netIncome <= currentExpense to avoid looping infinitely. This was not very robust, as this infinite result could then be used in another computation, which would lead to bogus results. It would be better to return Option[Int] to indicate that the function might not be computable and let the caller decide what to do. We would return None if it was not computable or Some(returnValue) if it was computable.

The following code is the new implementation for nbMonthsSaving, with the changed portions highlighted in bold:

def nbOfMonthsSaving(params: RetCalcParams, 
returns: Returns): Option[Int] = {
import params._
@tailrec
def loop(months: Int): Int = {
val (capitalAtRetirement, capitalAfterDeath) =
simulatePlan(returns, params, months)

if (capitalAfterDeath > 0.0)
months
else
loop(months + 1)
}

if (netIncome > currentExpenses)
Some(loop(0))
else
None
}

Now try to compile the project. This change breaks many parts of our project, but the Scala compiler is a terrific assistant. It will help us identify the portions of the code we need to change to make our code more robust.

The first error is in RetCalcSpec.scala, as shown in the following code:

Error:(65, 14) types Option[Int] and Int do not adhere to the type constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.CanEqual[Option[Int],Int]
actual should ===(expected)

This error means that the types in the actual should === (expected) expression do not match: actual is of the Option[Int]0 type, whereas expected is of the Int type. We need to change the assertion, as follows:

actual should ===(Some(expected))

You can apply the same fix for the second unit test. For the last unit test, we want to assert that None is returned instead of Int.MaxValue, as shown in the following code:

"not loop forever if I enter bad parameters" in {
val actual = RetCalc.nbOfMonthsSaving(params.copy(netIncome = 1000), FixedReturns(0.04))
actual should ===(None)
}

You can now compile and run the test. It should pass.

You are now able to model an optional value safely. However, sometimes it is not always obvious to know what None actually means. Why did this function return None? Was it because the arguments that were passed were wrong? Which argument was wrong? And what value would be correct? It would indeed be nice to have some explanation that comes along with the None in order to understand why there was no value. In the next section, we are going to use the Either type for this purpose.

主站蜘蛛池模板: 唐海县| 临武县| 府谷县| 兴安县| 东明县| 河池市| 滨海县| 西青区| 大英县| 高青县| 洛南县| 铁岭市| 高要市| 庄河市| 清水县| 尉氏县| 成武县| 竹溪县| 册亨县| 太谷县| 平阴县| 西平县| 南充市| 德江县| 股票| 涟水县| 咸宁市| 崇阳县| 和平县| 天全县| 周至县| 垦利县| 南城县| 安吉县| 吴桥县| 民丰县| 个旧市| 樟树市| 鄂尔多斯市| 开鲁县| 静海县|