LoginSignup
11
5

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-01-06

元ネタ

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)年あけましておめでとうございます。

view code

どうすれば「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

view code

改修

改修後
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

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>

view code

破壊的変更後

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>

view code

どうすれば「(Just 2018)」を防げたか

showを使わずtoStringAsを使う実装にしていれば破壊的変更後にエラーとなり検知可能

import Data.Int (toStringAs, decimal)

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

view code

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>

view code

どうすれば「Just 2018」を防げたか

全言語で「Optional(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年あけましておめでとうございます。

view code

破壊的変更後

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)年あけましておめでとうございます。

view code

どうすれば「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年あけましておめでとうございます。

view code

破壊的変更後

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年あけましておめでとうございます。

view code

Scala

破壊的変更前

object Main extends App {
    type Year = Int
    def happyNewYear(year: Year) {
        println(year + "年あけましておめでとうございます。")
        // これも同じ結果
        // println(s"${year}年あけましておめでとうございます。")
    }
    happyNewYear(2018)
}
出力
2018年あけましておめでとうございます。

view code

破壊的変更後

object Main extends App {
    type Year = Option[Int]
    def happyNewYear(year: Year) {
        println(year + "年あけましておめでとうございます。")
        // これも同じ結果
        // println(s"${year}年あけましておめでとうございます。")
    }
    happyNewYear(Some(2018))
}
出力
Some(2018)年あけましておめでとうございます。

view code

どうすれば「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年あけましておめでとうございます。
年が明けることはなかった。。。

view code

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年あけましておめでとうございます。

view code

破壊的変更後

typealias Year = Int?

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

view code

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年あけましておめでとうございます。

view code

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

全言語で「Optional(2018)」を防ぐ方法!

  • happyNewYear関数のテストコードを書く!
  • テストコードを書かないなら、安易にメジャーアップデートしない!
11
5
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
5