Java
Scala
オブジェクト指向
関数型言語
More than 5 years have passed since last update.

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


まとめ

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

実は僕も、関数型言語を理解するために日々悩んでいるんだ。

それだけ難しい概念だと思うけど、

ここを掌握できればプログラミングの質が上がると信じているよ。

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

体で感じてくれたかな?