88
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

Organization

第2章:What is 関数型言語?

Scalaの醍醐味は関数型言語とオブジェクト指向言語のハイブリッドだ!

今回はオブジェクト指向をやっている人には馴染みが薄い、
関数型言語について語るよ。

関数型言語って何?

関数を駆使してプログラミングできる言語が、関数型言語だ!
関数とはオブジェクト指向でいうと「メソッド」みたいなもんだ。
意味は違うけどね。数学的な例だと、

y = sin(x)

これは引数xを与えると、sinが計算して値をyに代入するんだ。
ここではsin(x)が関数だよ。

次にScalaで関数を作ってみるよ。

FunctionExample.scala
object FunctionExample{
  def main(args: Array[String]){

    var calculate = (x: Int) => x + 5 : Int

    printf("y=%s \n", calculate(1))
    printf("y=%s \n", calculate(2))
  }
}

実行すると以下のようになるよ。

$ scala FunctionExample.scala
y=6
y=7

FunctionExample.scalaで定義した関数は、calculate
引数に数値を与えると、その引数に5を足して返す関数だ。
これは、y=x+5という一次関数を表してるんだよ。

誤解を恐れずに言うと、Cの関数やJavaのメソッド
と同様に処理を表す部分でもある。
もちろん違いはあるので、関数型言語の特徴と言う形で述べていこう。

関数型言語の特徴

  • immutable (いみゅーたぶる)
  • 副作用がない
  • 関数はファーストクラス(第一級)

関数型言語の特徴を調べると、上記のような項目が必ず出てくる。
最初はよくわからないよね?
でも基本だから避けては通れないよ。

immutable(いみゅーたぶる)

immutableとはim(in) + mutableのことだ。
mutable?「変わる」ことだよ。immutable「変わらない」ことだね。
「変わる」って何が?変わるのは値だよ。

Scalaでこれを端的に表すキーワードが存在している。
varvalだ。

Mutable.scala
object Mutable{
  def main(args: Array[String]){
    var mutable = "Mutable"
    printf("%s ", mutable)

    mutable = "Change mutable"
    printf("%s ", mutable)
  }
}

実行すると、varで定義した変数mutableの値が切り替わることがわかるね。
これがmutableだ。

$ scala Mutable.scala
Mutable
Change mutable

次にimmutable。

Immutable.scala
object Immutable{
  def main(args: Array[String]){
    val immutable = "Immutable"
    printf("%s ", immutable)

    immutable = "Change immutable"
    printf("%s ", immutable)
  }
}

実行するとコンパイルエラーが発生する。

$ scala Immutable.scala
Immutable.scala:6: error: reassignment to val
immutable = "Change immutable"
          ^
one error found

immutable、即ちvalで定義した変数の値が変えられないことがわかったかな?

副作用がない

本来するべきことを作用とすれば、作用に伴って別のことをしてしまうのが副作用だ。

風邪をひいたらどうする?
風邪薬を飲むよね。飲んだら眠くなることない?
風邪薬では、風邪を治すことが作用、眠くなることが副作用だ。

副作用の具体例をソースで表してみるよ。

SideEffect.scala
object SideEffect{
  var total = 0

  def main(args: Array[String]){
    var add = (x:Int) => {
      total+=x
      total
    }

    printf("total=%s \n", add(1))
    printf("total=%s \n", add(1))
    printf("total=%s \n", add(1))
  }
}

実行すると以下のようになる。

$ scala SideEffect.scala
total=1
total=2
total=3

この例では、メソッドaddを引数1で3回呼んでいるが、
呼ばれるたびにtotalを足している。

そのため、addは呼ばれるたびに異なる戻り値を返す。
addはオブジェクトフィールドtotalにも作用していることを、
副作用と言う。

副作用のない例は以下となる。

NoneSideEffect.scala
object NoneSideEffect{
  def main(args: Array[String]){
    var add = (x: Int) => x + 5

    printf("add=%s \n", add(1))
    printf("add=%s \n", add(1))
    printf("add=%s \n", add(1))
  }
}
$ scala NoneSideEffect.scala
add=6
add=6
add=6

SideEffect.scalaはオブジェクト指向ぽく書いたけど、
まさしくオブジェクト指向は副作用の塊だね。

もちろん関数型言語を駆使すれば、副作用を全て排除できるわけではないよ。
IOに関わる部分は副作用だからね。
でもなくせる場所では副作用をなくそうぜ!

関数はファーストクラス(第一級)

ファーストクラス(第一級)とは、関数を型として扱えるってことなんだ。
文字列型とか数値型とかと同じにみなせる。
型として扱えるということは、var addみたいな変数に、関数を設定することが可能なんだよ。
JavaScriptのvar func = function(){......};と同じだね。

次にファーストクラスの利点。
- 関数の引数として渡せる
- 関数の戻り値として返せる

Javaにはできないことだよ。メソッドだけをオブジェクトとして返せないからね。
メソッドはオブジェクトありきなんだ。

関数の戻り値の例で見てみよう。

FirstClassFunction.scala
object FirstClassFunction{
  def main(args: Array[String]){
    val add = (x: Int) => {
      val _add = (y: Int) =>  y + 5
      _add(x)
    }

    printf("add=%s \n", add(1))
    printf("add=%s \n", add(2))
    printf("add=%s \n", add(1))
  }
}

実行してみよう!

$ scala SideEffect.scala
add=6
add=7
add=6

関数addの戻り値はなんと関数_addだ!
変数funcには、add()の戻り値_addが入っている。
そしてfunc(1)_add(1)を評価することと同じになる。
だからfunc(1)の結果は6になるのさ。

ちなみにファーストクラスのクラスは、オブジェクト指向のクラスとは
全く関係ないから注意してね!

最上級だ!!!

言語の翻訳ではよく「functions as first-class citizens」って言葉が出てきて
「第一級市民である関数」なんて訳されているよ。

関数とメソッドの違い

Java等のオブジェクト指向言語を使っていると、関数とメソッドの違いがわからないかもしれない。

メソッドはあくまでオブジェクトと関連付いているんだ。
オブジェクトの操作をメソッドと言う。

オブジェクトには状態(フィールド)があるんだけど、
状態によって戻り値が変わる。引数がなくても、
戻り値が変わることがあるんだ。

staticおじさんの作りしかしらなかったら、ゴメンナサイ!

一方関数は、引数に依存する。
引数が同じなら常に同じ戻り値を返し、
引数が変わったら別の戻り値を返す。
状態には依存させないよ。

文法上の違いもあるよ。

defキーワードはメソッドになる。

def add(x:Int, y: Int) = x + y

これは関数リテラルで、関数となる。

var add = (x:Int, y:Int) => x + y

まとめ

今回は関数型言語について語ってみたけど難しかったかな?

実は僕も、関数型言語を理解するために日々悩んでいるんだ。
それだけ難しい概念だと思うけど、
ここを掌握できればプログラミングの質が上がると信じているよ。

今回の、関数型言語のソースを実際に動かすことで、

体で感じてくれたかな?

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
88
Help us understand the problem. What are the problem?