元ネタ
Optional(2018)と表示された原因
Swift 2からSwift 3になった際に、DateComponents 構造体のyearやmonthプロパティがOptional 型になったため
他の言語でも同様の破壊的変更があった場合どうなるか?
Swift
破壊的変更前
typealias Year = Int
func happyNewYear(_ year: Year) {
print("\(year)年あけましておめでとうございます。")
}
happyNewYear(2018)
2018年あけましておめでとうございます。
破壊的変更後
typealias Year = Optional<Int>
に変更する
typealias Year = Optional<Int>
func happyNewYear(_ year: Year) {
print("\(year)年あけましておめでとうございます。")
}
happyNewYear(2018)
Optional(2018)年あけましておめでとうございます。
どうすれば「Optional(2018)」を防げたか
コンパイル時に下のような警告が出るのでそれをチェックする
warning: string interpolation produces a debug description for an optional value; did you mean to make this explicit?
OCaml
破壊的変更前
type year = int
let happy_new_year(year: year) =
Printf.printf "%d年あけましておめでとうございます。\n" year;;
happy_new_year(2018);;
2018年あけましておめでとうございます。
破壊的変更後
type year = int option
に変更する
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
改修
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年あけましておめでとうございます。
BuckleScript
破壊的変更前
type year = int
let happy_new_year(year: year) = Js.log {j|$year年あけましておめでとうございます。|j};;
happy_new_year(2018);;
2018年あけましておめでとうございます。
破壊的変更後
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
破壊的変更前
module Main where
import Prelude(($), (<>), show)
import TryPureScript
type Year = Int
happyNewYear :: Year -> String
happyNewYear x = show(x) <> "年あけましておめでとうございます。"
main =
render $ p (text (happyNewYear 2018))
<p>2018年あけましておめでとうございます。</p>
破壊的変更後
type Year = Maybe Int
に変更する
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)))
<p>(Just 2018)年あけましておめでとうございます。</p>
どうすれば「(Just 2018)」を防げたか
show
を使わずtoStringAs
を使う実装にしていれば破壊的変更後にエラーとなり検知可能
import Data.Int (toStringAs, decimal)
happyNewYear :: Year -> String
happyNewYear x = toStringAs decimal x <> "年あけましておめでとうございます。"
Elm
破壊的変更前
import Html exposing (text, div)
type alias Year = Int
happyNewYear: Year -> String
happyNewYear year = toString year ++ "年あけましておめでとうございます。"
main =
div []
[ text (happyNewYear 2018)
]
<div>2018年あけましておめでとうございます。</div>
破壊的変更後
type alias Year = Maybe Int
に変更する
import Html exposing (text, div)
type alias Year = Maybe Int
happyNewYear: Year -> String
happyNewYear year = toString year ++ "年あけましておめでとうございます。"
main =
div []
[ text (happyNewYear (Just 2018))
]
<div>Just 2018年あけましておめでとうございます。</div>
どうすれば「Just 2018」を防げたか
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年あけましておめでとうございます。
破壊的変更後
typedef Year = Option<Int>;
に変更する
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)年あけましておめでとうございます。
どうすれば「Some(2018)」を防げたか
全言語で「Optional(2018)」を防ぐ方法!
※事前にStd.is
を使って型チェックで対策できる可能性あり
Rust
破壊的変更前
type Year = u32;
fn main() {
happy_new_year(2018);
}
fn happy_new_year(year: Year) {
println!(
"{}年あけましておめでとうございます。",
year
);
}
2018年あけましておめでとうございます。
破壊的変更後
type Year = Option<u32>;
にする
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
改修
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年あけましておめでとうございます。
Scala
破壊的変更前
object Main extends App {
type Year = Int
def happyNewYear(year: Year) {
println(year + "年あけましておめでとうございます。")
// これも同じ結果
// println(s"${year}年あけましておめでとうございます。")
}
happyNewYear(2018)
}
2018年あけましておめでとうございます。
破壊的変更後
object Main extends App {
type Year = Option[Int]
def happyNewYear(year: Year) {
println(year + "年あけましておめでとうございます。")
// これも同じ結果
// println(s"${year}年あけましておめでとうございます。")
}
happyNewYear(Some(2018))
}
Some(2018)年あけましておめでとうございます。
どうすれば「Some(2018)」を防げたか
f 補間子を使えばエラーとなりチェック可能
println(f"${year}%d年あけましておめでとうございます。")
Go
GoにはOptional型がない
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年あけましておめでとうございます。
年が明けることはなかった。。。
Egison
EgisonにはOptional型がない
(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にはOptional型がない
: 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
破壊的変更前
typealias Year = Int
fun happyNewYear(year: Year) {
println("${year}年あけましておめでとうございます。")
}
fun main(args: Array<String>) {
happyNewYear(2018)
}
2018年あけましておめでとうございます。
破壊的変更後
typealias Year = Int?
fun happyNewYear(year: Year) {
println("${year}年あけましておめでとうございます。")
}
fun main(args: Array<String>) {
happyNewYear(2018)
}
2018年あけましておめでとうございます。
null
が許容されたため、null年あけましておめでとうございます。
が表示される可能性あり
TypeScript
破壊的変更前
type Year = number
function happyNewYear(year: Year) {
console.log(year + "年あけましておめでとうございます。");
}
happyNewYear(2018);
2018年あけましておめでとうございます。
破壊的変更後
type Year = number | null
function happyNewYear(year: Year) {
console.log(year + "年あけましておめでとうございます。");
}
happyNewYear(2018);
2018年あけましておめでとうございます。
null
が許容されたため、null年あけましておめでとうございます。
が表示される可能性あり
全言語で「Optional(2018)」を防ぐ方法!
-
happyNewYear
関数のテストコードを書く! - テストコードを書かないなら、安易にメジャーアップデートしない!