いろんな言語で「Optional(2018)年」やってみよう


# 元ネタ
* [Optional(2018)年あけましておめでとうございます](https://moneyforward.com/engineers_blog/2018/01/05/optional2018/)
* [マジレスすると『Optional(2018)年』を恐れる必要はない](https://qiita.com/koher/items/6c855ddbda8797af4605)

# Optional(2018)と表示された原因
Swift 2からSwift 3になった際に、DateComponents 構造体のyearやmonthプロパティがOptional 型になったため

# 他の言語でも同様の破壊的変更があった場合どうなるか?

## Swift
### 破壊的変更前
```swift
typealias Year = Int
func happyNewYear(_ year: Year) {
	print("\(year)年あけましておめでとうございます。")
}

happyNewYear(2018)
```
```:出力
2018年あけましておめでとうございます。
```
### 破壊的変更後
`typealias Year = Optional<Int>`に変更する

```swift
typealias Year = Optional<Int>
func happyNewYear(_ year: Year) {
	print("\(year)年あけましておめでとうございます。")
}

happyNewYear(2018)
```

```:出力
Optional(2018)年あけましておめでとうございます。
```
[view code](http://swift.sandbox.bluemix.net/#/repl/5a5146b6383944280cf6bd17)
### どうすれば「Optional(2018)」を防げたか
コンパイル時に下のような警告が出るのでそれをチェックする

```
warning: string interpolation produces a debug description for an optional value; did you mean to make this explicit?
```

## OCaml
### 破壊的変更前

```ocaml
type year = int
let happy_new_year(year: year) = 
    Printf.printf "%d年あけましておめでとうございます。\n" year;;
happy_new_year(2018);;
```
```:出力
2018年あけましておめでとうございます。
```
### 破壊的変更後
`type year = int option`に変更する

```ocaml
type year = int option
let happy_new_year(year: year) = 
    Printf.printf "%d年あけましておめでとうございます。\n" year;;
happy_new_year(Some 2018);;
```
```:破壊的変更後はエラーとなる
File "prog.ml", line 3, characters 88-92:
Error: This expression has type year = int option
       but an expression was expected of type int
```
[view code](https://ideone.com/Cw9YQE)

### 改修
```ocaml:改修後
type year = int option
let happy_new_year(year: year) = 
    let f o = match o with
	  | Some year -> (Printf.sprintf "%d年あけましておめでとうございます。\n" year)
	  | None -> "年は明けなかった。。。\n"
    in
    print_string(f year);;

happy_new_year(Some 2018);;
```
```:出力
2018年あけましておめでとうございます。
```

[view code](https://ideone.com/0mszM9)
## [BuckleScript](https://bucklescript.github.io/)
### 破壊的変更前
```bucklescript
type year = int
let happy_new_year(year: year) = Js.log {j|$year年あけましておめでとうございます。|j};;
happy_new_year(2018);;
```
```:出力
2018年あけましておめでとうございます。
```
### 破壊的変更後
```bucklescript
type year = int option
let happy_new_year(year: year) = Js.log {j|$year年あけましておめでとうございます。|j};;
happy_new_year(Some 2018);;
```
```:出力
2018年あけましておめでとうございます。
```
※`happy_new_year(None)`にすると`0年あけましておめでとうございます。`と表示される
### 破壊的変更時にエラーを出すには
`string_of_int`を使うことで破壊的変更時にエラーとなる

```
type year = int
let happy_new_year(year: year) = 
    Js.log(string_of_int year ^ {j|年あけましておめでとうございます。\n|j});;
happy_new_year(2018);;
```
```:出力
2018年あけましておめでとうございます。
```
```
type year = int option
let happy_new_year(year: year) = 
    Js.log(string_of_int year ^ {j|年あけましておめでとうございます。\n|j});;
happy_new_year(Some 2018);;
```
```:破壊的変更後はエラーとなる
Error: This expression has type year = int option
       but an expression was expected of type int
```

## [PureScript](http://www.purescript.org/)
### 破壊的変更前
```purescript
module Main where

import Prelude(($), (<>), show)
import TryPureScript

type Year = Int

happyNewYear :: Year -> String
happyNewYear x = show(x) <> "年あけましておめでとうございます。"

main =
    render $ p (text (happyNewYear 2018)) 
```
```html:出力
<p>2018年あけましておめでとうございます。</p>
```
[view code](http://try.purescript.org/?gist=53adbfcd2f4fdf7bc615947ab4fddf36&session=0800f2bd-3a01-e491-6b86-3f18dd7df05f&backend=core)
### 破壊的変更後
`type Year = Maybe Int`に変更する

```purescript
module Main where

import Prelude(($), (<>), show)
import Data.Maybe (Maybe(..))
import TryPureScript

type Year = Maybe Int

happyNewYear :: Year -> String
happyNewYear x = show(x) <> "年あけましておめでとうございます。"

main =
    render $ p (text (happyNewYear (Just 2018)))
```
```html:出力
<p>(Just 2018)年あけましておめでとうございます。</p>
```
[view code](http://try.purescript.org/?gist=1fed6ff6fa0360e21b345db3415767fd&session=0800f2bd-3a01-e491-6b86-3f18dd7df05f&backend=core)

### どうすれば「(Just 2018)」を防げたか
`show`を使わず`toStringAs`を使う実装にしていれば破壊的変更後にエラーとなり検知可能

```purescript
import Data.Int (toStringAs, decimal)

happyNewYear :: Year -> String
happyNewYear x = toStringAs decimal x <> "年あけましておめでとうございます。"
```
[view code](http://try.purescript.org/?gist=079251c099b736e3becf2ce74a273d67&session=0800f2bd-3a01-e491-6b86-3f18dd7df05f&backend=core)

## [Elm](http://elm-lang.org/)
### 破壊的変更前
```elm
import Html exposing (text, div)
type alias Year = Int

happyNewYear: Year -> String
happyNewYear year = toString year ++ "年あけましておめでとうございます。"
main =
    div []
    [ text (happyNewYear 2018)
    ]
```
```html:出力
<div>2018年あけましておめでとうございます。</div>
```

### 破壊的変更後
`type alias Year = Maybe Int`に変更する

```elm
import Html exposing (text, div)
type alias Year = Maybe Int

happyNewYear: Year -> String
happyNewYear year = toString year ++ "年あけましておめでとうございます。"
main =
    div []
    [ text (happyNewYear (Just 2018))
    ]
```
```html:出力
<div>Just 2018年あけましておめでとうございます。</div>
```
[view code](https://glot.io/snippets/ex4quhv0lk)

### どうすれば「Just 2018」を防げたか
[全言語で「Optional(2018)」を防ぐ方法！](#%E5%85%A8%E8%A8%80%E8%AA%9E%E3%81%A7optional2018%E3%82%92%E9%98%B2%E3%81%90%E6%96%B9%E6%B3%95)

## Haxe
### 破壊的変更前
```haxe
import haxe.ds.Option;
typedef Year = Int;

class Main {
    static function main() {
        happyNewYear(2018);
    }
    static function happyNewYear(year: Year) {
        trace(year + "年あけましておめでとうございます。");
        // これも同じ結果
        // trace('${year}年あけましておめでとうございます。');
    }
}
```
```:出力
2018年あけましておめでとうございます。
```
[view code](https://try.haxe.org/#89226)

### 破壊的変更後
`typedef Year = Option<Int>;`に変更する

```haxe
import haxe.ds.Option;
typedef Year = Option<Int>;

class Main {
    static function main() {
        happyNewYear(Some(2018));
    }
    static function happyNewYear(year: Year) {
        trace(year + "年あけましておめでとうございます。");
        // これも同じ結果
        // trace('${year}年あけましておめでとうございます。');
    }
}
```
```:出力
Some(2018)年あけましておめでとうございます。
```
[view code](https://try.haxe.org/#E3427)
### どうすれば「Some(2018)」を防げたか
[全言語で「Optional(2018)」を防ぐ方法！](#%E5%85%A8%E8%A8%80%E8%AA%9E%E3%81%A7optional2018%E3%82%92%E9%98%B2%E3%81%90%E6%96%B9%E6%B3%95)
※事前に`Std.is`を使って型チェックで対策できる可能性あり

## Rust
### 破壊的変更前
```rust
type Year = u32;

fn main() {
    happy_new_year(2018);
}

fn happy_new_year(year: Year) {
    println!(
        "{}年あけましておめでとうございます。",
        year
    );
}
```
```:出力
2018年あけましておめでとうございます。
```
[view code](https://play.rust-lang.org/?gist=379dfd61631d9cb13f67b7714fed02e7&version=stable)

### 破壊的変更後
`type Year = Option<u32>;` にする

```rust
type Year = Option<u32>;

fn main() {
    happy_new_year(Some(2018));
}

fn happy_new_year(year: Year) {
    println!(
        "{}年あけましておめでとうございます。",
        year
    );
}
```
```:破壊的変更後はエラーとなる
error[E0277]: the trait bound `std::option::Option<u32>: std::fmt::Display` is not satisfied
  --> src/main.rs:10:9
   |
10 |         year
   |         ^^^^ `std::option::Option<u32>` cannot be formatted with the default formatter; try using `:?` instead if you are using a format string
   |
   = help: the trait `std::fmt::Display` is not implemented for `std::option::Option<u32>`
   = note: required by `std::fmt::Display::fmt`

error: aborting due to previous error
```

### 改修
```rust:改修後
type Year = Option<u32>;

fn main() {
    happy_new_year(Some(2018));
}

fn happy_new_year(year: Year) {
    if let Some(year) = year {
        println!(
            "{}年あけましておめでとうございます。",
            year
        );
    } else {
        println!("年が明けることはなかった。。。")
    }
}
```
```:出力
2018年あけましておめでとうございます。
```
[view code](https://play.rust-lang.org/?gist=28395aa2fe07e2e60a74afa207ee948a&version=stable)


## Scala
### 破壊的変更前
```scala
object Main extends App {
	type Year = Int
	def happyNewYear(year: Year) {
		println(year + "年あけましておめでとうございます。")
		// これも同じ結果
		// println(s"${year}年あけましておめでとうございます。")
	}
	happyNewYear(2018)
}
```
```:出力
2018年あけましておめでとうございます。
```
[view code](https://ideone.com/lDEOyr)

### 破壊的変更後
```scala
object Main extends App {
	type Year = Option[Int]
	def happyNewYear(year: Year) {
		println(year + "年あけましておめでとうございます。")
		// これも同じ結果
		// println(s"${year}年あけましておめでとうございます。")
	}
	happyNewYear(Some(2018))
}
```
```:出力
Some(2018)年あけましておめでとうございます。
```
[view code](https://ideone.com/DynRn5)

### どうすれば「Some(2018)」を防げたか
f 補間子を使えばエラーとなりチェック可能

```scala
println(f"${year}%d年あけましておめでとうございます。")
```

## Go
GoにはOptional型がない

```go
package main

import (
	"errors"
	"fmt"
)

func happyNewYear(year uint) {
	fmt.Printf("%d年あけましておめでとうございます。\n", year)
}

func newYear2018() (uint, error) {
	return 2018, nil
}

func newYearNothing() (uint, error) {
	return -0, errors.New("年が明けることはなかった。。。")
}

func main() {
	{
		year, err := newYear2018()
		// errを使用せずに happyNewYear(year) を実行できない
		if err != nil {
			fmt.Println(err)
			return
		}
		happyNewYear(year)
	}
	{
		year, _ := newYearNothing()
		// `_` を使用することでエラーチェック回避することは可能
		happyNewYear(year)
	}
	{
		year, err := newYearNothing()
		if err != nil {
			fmt.Println(err)
			return
		}
		happyNewYear(year)
	}
}
```
```:出力
2018年あけましておめでとうございます。
0年あけましておめでとうございます。
年が明けることはなかった。。。
```
[view code](http://tpcg.io/kKQktb)
## [Egison](https://www.egison.org/)
EgisonにはOptional型がない

```egison
(define $happy-new-year
  (lambda $x
    (match (integer? x) bool
      {[,#t (S.append (show x) "年あけましておめでとうございます。")]})))

print (happy-new-year 2018)
```
```:出力
"2018年あけましておめでとうございます。"
```
※`(happy-new-year "hello")`などinteger型以外を引数に取ると`Error: failed pattern match`となる

## Factor
[Factor](http://factorcode.org/)にはOptional型がない

```factor
: happy-new-year ( year -- )
    number>string "年あけましておめでとうございます。" append print ;
2018 happy-new-year
```
```:出力
2018年あけましておめでとうございます。
```
※`"hello" happy-new-year`などnumber型以外を引数に取ると`Generic word >base does not define a method for the string class. Dispatching on object: "hello"`となる

## Kotlin
### 破壊的変更前
```kotlin
typealias Year = Int

fun happyNewYear(year: Year) {
    println("${year}年あけましておめでとうございます。")
}
fun main(args: Array<String>) {
    happyNewYear(2018)
}
```
```:出力
2018年あけましておめでとうございます。
```
[view code](https://glot.io/snippets/ex4d6auari)

### 破壊的変更後
```kotlin
typealias Year = Int?

fun happyNewYear(year: Year) {
    println("${year}年あけましておめでとうございます。")
}
fun main(args: Array<String>) {
    happyNewYear(2018)
}
```

```:出力
2018年あけましておめでとうございます。
```
[view code](https://glot.io/snippets/ex4d22ajeq)

`null`が許容されたため、`null年あけましておめでとうございます。`が表示される可能性あり

## TypeScript
### 破壊的変更前
```typescript
type Year = number

function happyNewYear(year: Year) {
    console.log(year + "年あけましておめでとうございます。");
}

happyNewYear(2018);
```
```:出力
2018年あけましておめでとうございます。
```
### 破壊的変更後
```typescript
type Year = number | null

function happyNewYear(year: Year) {
    console.log(year + "年あけましておめでとうございます。");
}

happyNewYear(2018);
```
```:出力
2018年あけましておめでとうございます。
```

[view code](https://www.typescriptlang.org/play/index.html#src=type%20Year%20%3D%20number%20%7C%20null%0D%0A%0D%0Afunction%20happyNewYear(year%3A%20Year)%20%7B%0D%0A%20%20%20%20console.log(year%20%2B%20%22%E5%B9%B4%E3%81%82%E3%81%91%E3%81%BE%E3%81%97%E3%81%A6%E3%81%8A%E3%82%81%E3%81%A7%E3%81%A8%E3%81%86%E3%81%94%E3%81%96%E3%81%84%E3%81%BE%E3%81%99%E3%80%82%22)%3B%0D%0A%7D%0D%0A%0D%0AhappyNewYear(2018)%3B%0D%0AhappyNewYear(null)%3B)

`null`が許容されたため、`null年あけましておめでとうございます。`が表示される可能性あり

# 全言語で「Optional(2018)」を防ぐ方法！
* `happyNewYear`関数の**テストコードを書く!**
* テストコードを書かないなら、**安易にメジャーアップデートしない！**