non-functional programming in scala

Post on 21-Jan-2018

5.074 Views

Category:

Software

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Non-Functional Programming in Scala

Naoki Takezoe@takezoen

BizReach, Inc

自己紹介

竹添 直樹: @takezoen

ビズリーチという会社でScalaを書いてます

OSS開発や技術書の執筆などもしています

主題

Scalaがもっと普及して欲しい

そうしないと自分が失業してしまう(重要!!)

そのためにはどうすればいいのか?

いままでやってきたこと

Scalaの本を書いたり、翻訳したりした

SI企業で業務システム開発の事例を作った

Scalaでオープンソースプロダクトを開発

Scala関連イベントのサポート

Webサービス企業でそれなりの規模でScalaを採用した

突き当たった壁

関数型プログラミング

関数型プログラミング in Scala

基本的には副作用を使った手続き型プログラミング言語

関数型言語由来の様々な機能を備えている

for内包表記、暗黙的なモナド

Scalaz、Cats、Slickなどの関数型ライブラリ

どこまで関数型に寄せるか?という選択を迫られる

プログラミングスタイルだけでなく、ライブラリやフレームワークの

選定から考慮しなくてはならない

振り返ってみる

業務での開発で起きたこと

OSS開発で起きたこと

業務での開発で起きたこと

時間

Scala力

業務での開発で起きたこと

最初はみんな初心者

時間

Scala力

業務での開発で起きたこと

Scala力アップ!!

最初はみんな初心者

時間

Scala力

業務での開発で起きたこと

Scala力アップ!!

最初はみんな初心者

後から入ってくる人つらい!!

時間

Scala力

ハードルの高さ

こうあって欲しい(気持ちは理解できる)

時間

Java力

業務での開発で起きたこと

最初はみんなScala初心者だった

初期のメンバーが少しずつレベルアップしてくる

関数型プログラミングが取り入れられてくる

時代によってコードの傾向が違う

後から入ってくる人ほどつらくなってしまう

振り返り

Scalaはプログラマの成長にあわせてスタイルを変えられる

長期間、固定のチームで開発するのであればチームの成長にあ

わせて変化していくことができる

メンバーの増加・入れ替わりが激しい場合は変化が大きいとどん

どんハードルが上がっていってしまう

OSS開発で起きたこと

Elasticsearch-Hadoop、Apache PredictionIOなど

手続き型バリバリのプログラミングスタイル

varやmutableコレクション、whileループ、returnや例外なども多

用されている

OSS開発で起きたこと

コミッターのスキルが低いのか?そんなことはない

バックグラウンド、モチベーションの違い

SparkのためにScalaを使っている

振り返り

言語としてではなくプロダクトとしてのニーズが存在する

○○を使えば□□ができる、というフックは増やしていくべき

関数型プログラミングへのモチベーションが低い状況でどうScalaを使うか?

方針

そもそもScalaは副作用のある手続き型プログラミング言語であ

り、モナドを意識せずに使えるように設計されている

Scalaの便利な機能を使いつつ、関数型プログラミングに寄せす

ぎないようにする

使うべきか?使わないべきか?

● var、while● mutableコレクション

● return● 例外

● null● Option.get● for内包表記

● 型クラス

考えてみよう!!

var、while

var、while

ループ処理などでフラグやアキュムレータなどに使いがち

takeWhileやfoldLeftなどで代用可能だが取っつきづらい

var line = reader.readLine()

while(line != null) {

...

line = reader.readLine()

}

var、while

ループ処理などでフラグやアキュムレータなどに使いがち

takeWhileやfoldLeftなどで代用可能だが取っつきづらい

var line = reader.readLine()

while(line != null) {

...

line = reader.readLine()

}

var、while

メソッド内での利用であれば許容する

valはJavaでfinalをつけるかどうかくらいの感覚で使い分けるの

がよさそう

mutableコレクション

mutableコレクション

ループしながら詰め替えるような処理で使いがち

val list = ...

val map = mutable.Map("some" -> 0, "none" -> 0)

list.foreach { x =>

if (x.nonEmpty) {

map.put("some", map("some") + 1)

} else {

map.put("none", map("none") + 1)

}

}

mutableコレクション

ループしながら詰め替えるような処理で使いがち

val list = ...

val map = mutable.Map("some" -> 0, "none" -> 0)

list.foreach { x =>

if (x.nonEmpty) {

map.put("some", map("some") + 1)

} else {

map.put("none", map("none") + 1)

}

}

mutableコレクション

メソッド内での利用であれば許容する

戻り値として返す際にimmutableなコレクションに変換する

使わなくても済むものはimmutableなコレクションを使うように啓

蒙していく

val map = list.groupBy(_.nonEmpty)

.map { case (nonEmpty, values) =>

if(nonEmpty) "some" -> values.size

else "none" -> values.size

}

return

return

Ealry returnやループ処理中からのreturnなどが使われがち

def hello(names: Seq[String]): String = {

if(name.isEmpty) return ""

...

}

戻り値の型を明記しないといけなくなる

コンパイル後に例外(ControlThrowable)で実現されるケースが

ある

return

Ealry returnやループ処理中からのreturnなどが使われがち

def hello(names: Seq[String]): String = {

if(name.isEmpty) return ""

...

}

戻り値の型を明記しないといけなくなる

コンパイル後に例外(ControlThrowable)で実現されるケースが

ある

return

実際に問題になるケースは少ないので許容してもよいのでは

そもそも例外をThrowableでキャッチしない

try {

...

} catch {

case NonFatal(t) => ...

}

例外

例外

エラーを戻り値で返すか?Eitherなどで返すか?

def findUser(): Either[Exception, User] = {

try {

val user: User = ...

Right(user)

} catch {

case e: Exception => Left(e)

}

}

例外

エラーを戻り値で返すか?Eitherなどで返すか?

def findUser(): Either[Exception, User] = {

try {

val user: User = ...

Right(user)

} catch {

case e: Exception => Left(e)

}

}

例外

Eitherで返そうとすると

例外の発生は防げないので変換が必要になる

for内包表記やEitherTなどが登場してしまう

通常は積極的に例外に倒してしまってもよいのでは?

ただし非同期処理の場合は別

null

null

null

使うべき理由がまるでない

がくぞー先生に消されてしまう

Optionを使う

Javaライブラリを使う部分は仕方ない

Option.get

Option.get

値が必ず入っているはずだと直接getしがち

Noneチェックしてからgetしがち

val opt: Option[String] = ...

if(opt.isDefined){

val str = opt.get

...

}

Option.get

値が必ず入っているはずだと直接getしがち

Noneチェックしてからgetしがち

val opt: Option[String] = ...

if(opt.isDefined){

val str = opt.get

...

}

Option.get

opt.getOrElse("") などとする人が出現

getして明示的にエラーになる方がマシではある

mapやforeachなどを使うよう啓蒙していく

for内包表記

for内包表記

モナモナするときに使う

for {

project <- findProject(projectId)

user <- findUser(userId)

} yield {

registerComment(project, user, comment)

}

for内包表記

モナモナするときに使う

for {

project <- findProject(projectId)

user <- findUser(userId)

} yield {

registerComment(project, user, comment)

}

for内包表記

無理して使わない

コレクション操作やFutureのチェーンをシンプルに記述するため

に使う

その場合もmapやflatMapのシンタックスシュガーという認識があ

ればよい

「モナド」って言わない

型クラス

型クラス

implicitの使い方の一種

implicit val userInfoReads = Json.reads[UserInfoInfo]

r.body.validate[UserInfo].fold(

error => ...,

form => ...

)

sealed trait JsValue extends JsReadable {

def validate[A](implicit rds: Reads[A]): JsResult[A]

}

型クラス

implicitの使い方の一種

implicit val userInfoReads = Json.reads[UserInfoInfo]

r.body.validate[UserInfo].fold(

error => ...,

form => ...

)

sealed trait JsValue extends JsReadable {

def validate[A](implicit rds: Reads[A]): JsResult[A]

}

型クラス

フレームワークやライブラリで使っているケースが多い

使うだけであれば意識する必要はない(CanBuildFromを意識し

なくてもScalaのコレクションは使える)

play-jsonのReads/Writesのように自分で実装しないといけない

ケースもあるが、そこまで躓くことはない印象(面倒なのはさてお

き...)

「型クラス」って言わない

結果

● var、while● mutableコレクション

● return● 例外

● null● Option.get● for内包表記

● 型クラス

最終的にはケースバイケース

これはあくまでも判断の一例

プロダクトの方向性、今後のチーム運営も考えて決める

チームのスキルが一様であればそれにあわせればよい

Scalaを使う人がもっと増えて欲しい

最初はみんな初心者だった

Scalaで楽しくプログラミングをして欲しい

関数型プログラミングに興味のある人だけでなく、

それ以外の人たちにもScalaを使って欲しい

Scalaを使っているのにというジレンマを感じることもある

いろんな使い方ができるのもScalaの良いところ

Scalaユーザが増えることで自分たちの選択肢も広がる

ご静聴ありがとうございました

top related