yoshikit1996’s diary

日々勉強したことの備忘録です。

Scalaアンチパターン

過去に自分が書いたScalaコードのアンチパターンをまとめました。

定数名が大文字

Scalaでは定数名はパスカルケースで書きます。

// ダメ
val MAX = 100
val MIN = 1
val HOGE_HOGE = "hogehoge"

// 良い
val Max = 100
val Min = 1
val HogeHoge = "hogehoge"

map + getOrElse

map(???).getOrElse(???)は冗長です。foldを使ってシンプルに記述することができます。

// ダメ
object Main extends App{
    val option = Some("hoge")
    val str = option.map(_.toUpperCase).getOrElse("")
    
    println(str)
}
// 良い
object Main extends App{
    val option = Some("hoge")
    val str = option.fold("")(_.toUpperCase)
    
    println(str)
}

returnを使う

returnはScalaらしくないので使うべきではありません。
Scalaの主な構成要素は式です。return文は異物感がします。

object Main extends App{
    
    def hoge(isHogeHoge: Boolean): String = {
        if(isHogeHoge) return "hogehoge"
        return ""
    }
    
    println(hoge(true))
}

get

option.getとすると例外が発生する可能性があります。getOrElseやmapで代用すべきです。

1文字の変数名"l"

Either型の値に対してmatchを使う場合、Leftにマッチさせる変数名に"l"(エル)を使いたくなる場合があると思います。
しかし、"I"(アイ)や"1"(イチ)と区別しづらくなるのでダメだと思います。

either match {
  case r: Right[???, ???] => ???
  case l: Left[???, ???] => ???
}

他にも、0(ゼロ)とO(オー)も識別しずらいと思います。スコープが短いと、使いたくなるのですが。。。

Either

Eitherはモナドでないので使いづらいです。
EitherよりOptionを使うべきです。

publicメソッドに戻り値の型を書かない

publicメソッドは外部のクラスから使われるので戻り値の型は書くべきです。

// ダメ
def hoge(fuga: String) = ???

// 良い
def hoge(fuga: String): String = ???

冗長な型アノテーション

// ダメ
val x: HashMap[Int, String] = new HashMap[Int, String]()
// 良い
val x = new HashMap[Int, String]()
val x: Map[Int, String] = new HashMap()

コップ本には、次のようなことが書かれています。

一般にドキュメントが役に立つのは、簡単にはわからないことを示しているときだ。

型が簡単にはわからないときに、型アノテーションをつけるべきです。

末尾再帰に@tailrecアノテーションをつけない

@tailrecアノテーションを使うと一目で末尾再帰を使っていることがわかります。

タプルで_1とか_2を使う

tuple._1とtuple._2とすると、意味がわかりにくいです。パターンマッチを使った方が意味がわかりやすいと思います。

object Main extends App{
    val prefectureCaptals = List(
        ("滋賀", "大津"),
        ("三重", "津"),
        ("兵庫", "神戸")
    )
    
    // ダメ
    prefectureCaptals.map( pc =>
        println(pc._1 + " : " + pc._2)
    )
    
    // 良い
    prefectureCaptals.map(_ match {
        case (prefecture, captal) => println(prefecture + " : " +captal)
    })
    
    // 良い
    val (prefecture, capital) = prefectureCaptals.head
    println(prefecture + " : " + capital)
    
    // 良い
    for((prefecture, capital) <- prefectureCaptals){
        println(prefecture + " : " + capital)
    }
}

foldLeftよりreduceLeft

初期値が不要な場合はfoldLeftではなく、reduceLeftを使うべきです。

object Main extends App{
    val list = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    // ダメ
    val sum1 = list.foldLeft(0){_ + _}
    // 良い
    val sum2 = list.reduceLeft{_ + _}
}