めんどくさくない scala #kwkni_scala

73
めんどくさくない Scala #kwkni_scala @seratch Kazuhiro Sera 1

Upload: kazuhiro-sera

Post on 31-May-2015

7.309 views

Category:

Technology


3 download

DESCRIPTION

怖くない Scala 勉強会での発表資料です。 http://connpass.com/event/3420/

TRANSCRIPT

Page 3: めんどくさくない Scala #kwkni_scala

まず、あなたと Scala の出会いを思い出してみてください。

3

Page 4: めんどくさくない Scala #kwkni_scala

引用しますね...

Team Geek ―Googleのギークたちはいかにしてチームを作るのか

[単行本(ソフトカバー)]

Brian W. Fitzpatrick (著), Ben Collins-

Sussman (著), 角 征典 (翻訳)

出版社: オライリージャパン (2013/7/20)

ISBN-10: 4873116309ISBN-13: 978–4873116303発売日: 2013/7/20

4

Page 5: めんどくさくない Scala #kwkni_scala

“初心者にとってソフトウェアとはどのようなものだろうか?”

“ソフトウェアはユーザーのことを歓迎しているだろうか?”

Team Geek 「6.1.1 第一印象に注目する」より引用

5

Page 6: めんどくさくない Scala #kwkni_scala

“使ってもらいやすくなっているだろうか?”

“熟練者にとって使いやすそうで実用的なものになっているだろうか?”

Team Geek 「6.1.1 第一印象に注目する」より引用

6

Page 7: めんどくさくない Scala #kwkni_scala

“すぐに生産性の向上をアピールできるだろうか?”

“それとも学習曲線が急で何度も涙が出るようなものだろうか?”

Team Geek 「6.1.1 第一印象に注目する」より引用

7

Page 8: めんどくさくない Scala #kwkni_scala

“もっと具体的に言うと、最初の 30 秒間のユーザーエクスペリエンス

はどのようなものだろうか?”

“ここでは感情的な答えが聞きたい。1 分後にどのように感じるだろうか?”

Team Geek 「6.1.1 第一印象に注目する」より引用

8

Page 9: めんどくさくない Scala #kwkni_scala

ここで質問。

9

Page 10: めんどくさくない Scala #kwkni_scala

初めて Scala でコードを書いてみたときどうだったか覚えていますか?

とりあえず Hello World を print した後次に何をやったっけ?何を調べたっけ?

10

Page 11: めんどくさくない Scala #kwkni_scala

フレームワーク、ライブラリ、ツール・・どれもすんなりと扱えて、すごく便利で「これは捗るwww」と思いましたか?それとも Try & Error で 頑張りましたか?

11

Page 12: めんどくさくない Scala #kwkni_scala

自分がまだ不勉強なせいなのかそれとも ”それ” が不親切だからなのか

初心者には区別がつかない。

入口は、極力ハマりポイントをなくしてすぐに試せる親切な手順が書いてあって“ゆとり仕様” くらいでちょうどいい。

12

Page 13: めんどくさくない Scala #kwkni_scala

そこを補完する Scala 逆引きレシピはすばらしいですね!今すぐ買いましょう!!

Scala逆引きレシピ (PROGRAMMER’S RECiPE)

[単行本(ソフトカバー)]

竹添 直樹 (著), 島本 多可子 (著)

出版社: 翔泳社 (2012/7/3)

言語 日本語ISBN-10: 4798125415ISBN-13: 978–4798125411発売日: 2012/7/3

13

Page 14: めんどくさくない Scala #kwkni_scala

で、ここからが本題。

14

Page 15: めんどくさくない Scala #kwkni_scala

“「Scala は捗る!」を根付かせたい”

Scala は書いていて楽しいし、一番好きな言語だけどいくつか面倒なことや残念なことがあります。

そのめんどくさいことを減らすための提案とそれを意識して私がつくっているものを

少し紹介したいと思います。

15

Page 16: めんどくさくない Scala #kwkni_scala

一般論としてのめんどくささ

16

Page 17: めんどくさくない Scala #kwkni_scala

めんどくさいのはダメ・めんどくさいものを頑張って使うのは有害である・本題に集中できず、他のミスを誘発しやすい・奥が深い症候群に陥りやすい・今まで投資した時間を正当化するために固執しがち・たいがいそのノウハウのポータビリティは低い・そもそも楽しくない(人生は短い)

 結果、確実に生産性が低下する(自明)17

Page 18: めんどくさくない Scala #kwkni_scala

Scala でめんどくさいこと?

18

Page 19: めんどくさくない Scala #kwkni_scala

すぐに思いつくこと...

・コンパイルが遅すぎる・Scala、sbt バージョン非互換・言語仕様の学習が大変そう・スタイルが統一されなさそう・そもそも JVM な時点でちょっと(ry

19

Page 20: めんどくさくない Scala #kwkni_scala

Compile! Compile! Compile!

20

Page 21: めんどくさくない Scala #kwkni_scala

コンパイルが遅すぎる

・良いマシンを...(震え声)・sbt の ~;compile とか ~;test でごまかす・コンパイルが遅くなる言語機能を避ける...

・何でもかんでもコンパイルしない(個人の意見です)

「何でもかんでもコンパイルしない」について Skinny

Framework のデモをして説明します

21

Page 22: めんどくさくない Scala #kwkni_scala

デモ:Yeoman って知ってますか?Node.js で動く Rails generator 的なもの。

Windows でも Node と Ruby 入れれば使えるはず。

brew install nodejs npm install -g yo npm install -g generator-skinny mkdir hello-skinny cd hello-skinny yo skinny ./skinny run

22

Page 23: めんどくさくない Scala #kwkni_scala

もう重たくない Scala 開発・Controller、Model は Jetty 起動なしでテスト可能・DB 連携は実行せずに正しさを確認できないので、お決まりな CRUD は(ある程度)動的でもよい・View の調整が圧倒的に多いので、いちいちコンパイルせず実行時エラーで確認(& Controller のテストを CI)・CoffeeScript なども開発 mode は実行時変換(予定)・production だけ Scalate precompile も可能・サンプルはこちらにあります

23

Page 24: めんどくさくない Scala #kwkni_scala

24

Page 25: めんどくさくない Scala #kwkni_scala

Scala、sbt の非互換・今使っているものは足かせにならないか?・ライブラリ、sbt プラグインは厳選した方がよい・ワーストケースで移行が容易な実装・テストにしておく・ ほとんどいじらない共通部分はあえて Java で書いてメンテコストを抑える(Scala 側でラップは必要)・フリーライダーで乗り切るのは多分無理(GitHub 時代の言語、自ら貢献する姿勢で)

25

Page 26: めんどくさくない Scala #kwkni_scala

26

Page 27: めんどくさくない Scala #kwkni_scala

言語仕様の学習が大変そう・最低限必要なレベルまではそれほど大変ではないはず・コップ本の前にもっとライトな本を流してもよい・”Scala for the impatient” あたりがオススメ・scalatutorials.com で手を動かすとか・困ったら scalajp の ML で質問(怖くない)

27

Page 28: めんどくさくない Scala #kwkni_scala

TMTOWTDI?“There’s more than one way to do it”

28

Page 29: めんどくさくない Scala #kwkni_scala

スタイルが統一されなさそう・書き方は基本的に Scala Style Guide に従う・scalariform は問答無用で適用する・汎用コーディング規約は存在しないので、チームの中での共通認識をすりあわせていく・理解度の一番低い人に合わせすぎない・何かに感染している人を周りは生暖かく見守る・歩み寄る 、ストレスを溜めない

29

Page 30: めんどくさくない Scala #kwkni_scala

30

Page 31: めんどくさくない Scala #kwkni_scala

そもそも JVM な時点で(ry

・LL だったらもっと簡単なのに... と思われたら負け・sbt 初回起動... 巨大な依存ライブラリは極力減らす・cs や g8 は好きだけど sbt だけで済まないのは微妙...

・ライブラリを作るなら REPL で簡単に試せることは重視する(import はなるべく少なく)・今日から Scala を始めたとしてどう思うか?

31

Page 32: めんどくさくない Scala #kwkni_scala

あなたの “Scala ではこれが普通” はただ慣れてしまっただけかもしれない...

一度、見直してみると...

32

Page 33: めんどくさくない Scala #kwkni_scala

ここからは宣伝。

33

Page 34: めんどくさくない Scala #kwkni_scala

RDB とのやり取り

34

Page 35: めんどくさくない Scala #kwkni_scala

 No More SQL? Really?

・「SQL ならもう分かってるけど、このライブラリでどう書けばいいか分からん... ドキュメントにないな... サンプル探すか... ない... ソース読むか...」(時間の無駄)・Anorm のコンセプト “You don’t need another DSL

to access relational databases.” 自体には共感する・RDB 使ってて SQL と手を切れるなんて幻想ですよね

35

Page 36: めんどくさくない Scala #kwkni_scala

ScalikeJDBC

・SQL 書きたいときは sql”select name from

companies where id = ${id}” のように書く・SQLInterpolation では必ずバインド変数になるので

SQL インジェクション脆弱性がないと断言できる・1.6 から select.from(Company as c).where.eq(c.id,

123) のように SQL から乖離しない DSL をサポート・QueryDSL は IDE での補完にも強い(Dynamic +

macros に時代が追いついてないけど)

36

Page 37: めんどくさくない Scala #kwkni_scala

SQLInterpolation

val ids = Seq(1, 2, 3) case class Member(id: Long, name: Option[String]) DB readOnly { implicit s => sql”select id, name from members where id in (${ids})” .map { rs => Member(rs.long(“id”), rs.stringOpt(“name”)) } .list.apply() } object Member extends SQLSyntaxSupport[Member] val m = Member.syntax(“m”) DB readOnly { implicit s => sql”select ${m.result.*} from ${Member as m} where ${m.id} in (${ids})” .map { rs => Member(rs.long(“id”), rs.stringOpt(“name”)) } .list.apply()}

37

Page 38: めんどくさくない Scala #kwkni_scala

QueryDSL

val ids = Seq(1, 2, 3) case class Member(id: Long, name: Option[String]) object Member extends SQLSyntaxSupport[Member] val m = Member.syntax(“m”) DB readOnly { implicit s => withSQL { select.from(Members as m).where.in(m.id, ids) } .map { rs => Member( id = rs.long(m.resultName.id), name = rs.stringOpt(m.resultName.name)) } .list.apply() }

38

Page 39: めんどくさくない Scala #kwkni_scala

「あれ?わかりやすい!」「めんどくさくなさそう!」

(心の声)

39

Page 40: めんどくさくない Scala #kwkni_scala

いろんな SQL を...

// Squeryl join(Student, Club.leftOuter)((s, c) => where(c.map(_.id) isNull) select(s) on(s.clubId === c.map(_.id)))

// Slick for { (s, c) <- Student leftJoin Club.map(_.??) on (_.clubId === _._1) if c._1.isNull } yield (s, c)

// ScalikeJDBC val (s, c) = (Student.syntax(“s”), Club.syntax(“c”)) select.from(Student as s).leftJoin(Club as c).on(s.clubId, c.id).where.isNull(c.id)

select * from students s left join clubs c on s.club_id = s.id where c.id is null

40

Page 41: めんどくさくない Scala #kwkni_scala

 one-to-x API

・one-to-x API が実は結構便利・sql”...”.one(..).toMany(...).list.apply() のようにして複数のテーブルを join してもきれいに取ってこれる・one-to-one は無制限だが one-to-manies は現状だと最大 5 個のテーブルまで結合可能という制限がある(実装依存なので必要なら要望ください)

41

Page 42: めんどくさくない Scala #kwkni_scala

one-to-x 例 val pg: Option[Programmer] = withSQL { select.from(Programmer as p) .leftJoin(Company as c).on(p.companyId, c.id) .leftJoin(ProgrammerSkill as ps).on(ps.programmerId, p.id) .leftJoin(Skill as s).on(sqls.eq(ps.skillId, s.id).and.isNull(s.deletedAt)) .where.eq(p.id, id).and.isNull(p.deletedAt) }.one(Programmer(p, c)) .toMany(Skill.opt(s)) .map { (pg, skills) => pg.copy(skills = skills) } .single.apply()

// one(...).toOne(...) // one(...).toManies(...)

42

Page 43: めんどくさくない Scala #kwkni_scala

Debugging & Testing

・ソースコードを自動生成する sbt プラグインもある(sbt “scalikejdbc-gen [table-name]”)・ActiveRecord のようなクエリログ出力、ログ出力だけでなく Fluentd など外部に送ったりもできる・テスト重視、AutoRollback と Fixture を ScalaTest、spec2 に提供している

43

Page 44: めんどくさくない Scala #kwkni_scala

クエリログ

[debug] s.StatementExecutor$$anon$1 - SQL execution completed

[Executed SQL] select * from users where email = '[email protected]'; (2 ms)

[Stack Trace] ... models.User$.findByEmail(User.scala:26) controllers.Projects$$anonfun$index$1$$anonfun$apply$1$$anonfun$apply$2.apply(Projects.scala:20) controllers.Projects$$anonfun$index$1$$anonfun$apply$1$$anonfun$apply$2.apply(Projects.scala:19) controllers.Secured$$anonfun$IsAuthenticated$3$$anonfun$apply$3.apply(Application.scala:88) controllers.Secured$$anonfun$IsAuthenticated$3$$anonfun$apply$3.apply(Application.scala:88) play.api.mvc.Action$$anon$1.apply(Action.scala:170)

・遅いクエリの内容だけでなく、そのクエリがコードのどこで発行されたかまで分かる

44

Page 45: めんどくさくない Scala #kwkni_scala

ScalaTest 例

class MemberSpec extends fixture.FlatSpec with AutoRollback { // fixture loading before each test override def fixture(implicit session: DBSession) { sql”insert into members values (1, ‘Alice’)”.update.apply() }

it should “work” in { implicit session => // do something and will be rolled back } }

・fixture パッケージの Spec であれば AutoRollback

trait を mixin して fixture と auto rollback が可能

45

Page 46: めんどくさくない Scala #kwkni_scala

specs2 例

object MemberSpec extends Specification { trait AutoRollbackWithFixture extends AutoRollback { // fixture loading before each test override def fixture(implicit session: DBSession) { sql”insert into members values (1, ‘Alice’)”.update.apply() } }

“it should work” in new AutoRollbackWithFixture { // do something and will be rolled back }

・unit は以下の通り、acceptance スタイルもサポート(ただし case class xxx() というちょっと変態的な...)

46

Page 49: めんどくさくない Scala #kwkni_scala

Go Reactive!・non-blocking な ScalikeJDBC-Async もあるよ・@mauricio の postgresql/mysql-async

・直接使うと結構めんどうなのを頑張って吸収している・トランザクションも map/for 式 で表現可能・JDBC ほど枯れてないので、ガチで使うなら人柱覚悟で

49

Page 50: めんどくさくない Scala #kwkni_scala

気に入ったら今すぐに http://git.io/scalikejdbc へ行って

star を押してください!!!

スター乞食

50

Page 51: めんどくさくない Scala #kwkni_scala

普通の Web アプリ開発

51

Page 52: めんどくさくない Scala #kwkni_scala

Play2 で管理画面?

・正直、いろいろ無理あるよね...

・Servlet 捨てなくていいとき多いよね...

・Play2 は C10K 問題を克服するための次世代の Web フレームワークを目指してわざわざリライトされたことを思い出そう(Play の ML で宣言)・個人的には Play2 は JSON API サーバなどが最も適していると思っている

52

Page 53: めんどくさくない Scala #kwkni_scala

なら Scalatra で

・Sinatra よりは機能が豊富とはいえ、同じジレンマ・色々やり方決めたり、他のものと組み合わせたり・・・管理画面みたいなお決まりのものなら、色々考えたり、決めごとつくったり、検証したりせずに済ませたい・でもフレームワークによる制限があると困る

53

Page 54: めんどくさくない Scala #kwkni_scala

Go Skinny!

・”Scala on Rails” を目指している Web フレームワーク(成り立ちはどちらかというと Padrino 的)・Scalatra + ScalikeJDBC を土台とし、それらをスムーズに連携させる glue code でできている・ View Template の Precompile を否定、コンパイル待ちを減らしてサクサク開発できることを重視・テスタビリティを重視(session の mock もサポート)

54

Page 55: めんどくさくない Scala #kwkni_scala

 Rails 的 Controller

・Controller は ScalatraFilter の拡張、Rails のアレがだいたいある(まだないものもこれから随時対応)・Validation は Command ではなく独自・Controller にルーティング設定を埋め込まず、メソッド定義するだけにするスタイルを推奨・SkinnyResource という Rails の ActiveResource 相当もあって ROA なアプリならこれが楽、Rails scaffold 互換で XML、JSON レスポンスの URL まで自動生成

55

Page 56: めんどくさくない Scala #kwkni_scala

Controller & Routing class MembersController extends SkinnyController { def index = { set(“members”, Member.findAll()) // set in the request scope render(“/members/index”) // expects /views/members/index.html.ssp } }

val members = new MembersController with Routes { get(“/members/?”)(index).as(‘index) // ‘index: action name } class ScalatraBootstrap extends SkinnyLifeCycle { override def initSkinnyApp(ctx: ServletContext) { ctx.mount(members, “/*”) } }

56

Page 57: めんどくさくない Scala #kwkni_scala

View

// WEB-INF/views/members/index.html.ssp <%@val members: Seq[Member] %> <ul> #for (member <- members) <li>${member.name}</li> #end </ul>

// WEB-INF/views/members/index.html.scaml -@val members: Seq[Member] %ul - for (member <- members) %li #{member.name}

57

Page 58: めんどくさくない Scala #kwkni_scala

before/afterAction

class MembersController extends SkinnyController { protectFromForgery() beforeAction(only = Seq(‘index)) { set(“members”, Member.findAll()) } def index = { render(“/members/index”) } }

val members = new MembersController with Routes { get(“/members/?”)(index).as(‘index) }

58

Page 59: めんどくさくない Scala #kwkni_scala

Form/Validation class MembersController extends SkinnyController { def createForm = validation( paramKey(“name”) is required, paramKey(“companyId”) is required & numeric)

def create = { if (createForm.validate()) { val id = Member.createWithPermittedAttributes(params.permit( “name” -> ParamType.String, “companyId” -> ParamType.Long)) flash += “notice” -> “Created!” redirect(s”/members/${id}”) } else { render(“/members/new”) } } }

59

Page 60: めんどくさくない Scala #kwkni_scala

「あれ?わかりやすい!」「めんどくさくなさそう!」

(心の声)

60

Page 61: めんどくさくない Scala #kwkni_scala

SkinnyResource object MembersController extends SkinnyResource { protectFromForgery() override def model = Member override def resourcesName = “members” override def resourceName = “member” override def createForm = validation(paramKey(“name”) is required) override def createFormStrongParameters = Seq(“name” -> ParamType.String) override def updateForm = validation(paramKey(“name”) is required) override def updateFormStrongParameters = Seq(“name” -> ParamType.String) }

class ScalatraBootstrap extends SkinnyLifeCycle { override def initSkinnyApp(ctx: ServletContext) { ctx.mount(MembersController, “/*”) } }

61

Page 62: めんどくさくない Scala #kwkni_scala

SkinnyResource URIs

GET /members GET /members/ GET /members.xml GET /members.json GET /members/{id} GET /members/{id}.xml GET /members/{id}.json GET /members/new POST /members POST /members/ GET /members/{id}/edit POST /members/{id} PUT /members/{id} PATCH /members/{id} DELETE /members/{id}

62

Page 63: めんどくさくない Scala #kwkni_scala

SkinnyResource Views

src/main/webapp/WEB-INF layouts/default.ssp views/members/index.html.ssp views/members/new.html.ssp views/members/edit.html.ssp views/members/show.html.ssp

・SkinnyResource の場合は、以下のような命名規約で

ssp/scaml/jade などのファイルを置く

63

Page 64: めんどくさくない Scala #kwkni_scala

「クソ便利すぎワロタwww」「めんどくさくなさそう!」

(心の声)

64

Page 65: めんどくさくない Scala #kwkni_scala

65

Page 66: めんどくさくない Scala #kwkni_scala

 Skinny ORM

・ActiveRecord 的な簡便さを追求・単体でも利用可能(Play2 でも使える)・裏側は ScalikeJDBC、その機能もフルで使える・var なしで entity を定義できる・Association はなるべく join でとってくる思想・Eager loading、ネストした Association は未対応・FactoryGirl(DB テストデータ作成サポート)・DBSeeds でお手軽 migration、ちゃんとしたのも予定

66

Page 67: めんどくさくない Scala #kwkni_scala

Model case class Member(id: Long, name: Option[String]) object Member extends SkinnyCRUDMapper[Member] { def extract(rs: WrappedResultSet, m: ResultName[Member]) = new Member( id = rs.long(m.id), name = rs.stringOpt(m.name) ) } Member.createWithAttribute(‘name -> “Alice”)

Member.findAllBy(sqls”name is not null”) Member.where(‘name -> “Alice”).limit(10).offset(0) val member = Member.findById(123) member.map(_.copy(name = “Bob”).save())

Member.updateById(123).withAttributes(‘name -> “Bob”) Member.deleteById(234)

67

Page 68: めんどくさくない Scala #kwkni_scala

FactoryGirl

// src/test/resources/factories.conf member { name=”Anonymous” }

// XXXSpec.scala val anon: Member = FactoryGirl(Member).create() val chris: Member = FactoryGirl(Member).create(‘name -> “Chris”) val factory = FactoryGirl(Member).withValues( ‘createdAt -> new DateTime(2011, 6, 22, 13, 46)) val eric: Member = factory.create(‘name -> “Eric”)

・factory_pal は instance をつくるだけ、こっちは DB にデータ投入もしてくれる(本来の #create)

68

Page 69: めんどくさくない Scala #kwkni_scala

Associations

case class Member(id: Long, name: Option[String], companyId: Option[Long], company: Option[Company] = None, skills: Seq[Skill] = Nil)

object Member extends SkinnyCRUDMapper[Member] { belongsTo[Company](Company, (m, c) => m.copy(company = c)).byDefault val skills = hasManyThough[Skill](MemberSkill, Skill, (m, ss) => m.copy(skills ss)) def extract(rs: WrappedResultSet, m: ResultName[Member]) = new Member( rs.long(m.id), rs.stringOpt(m.name), rs.longOpt(m.companyId) ) } Member.findById(123) // with company Member.joins(Member.skills).findById(123) // with company, skills

69

Page 70: めんどくさくない Scala #kwkni_scala

 Skinny の今後・C10K クリアしないとまずいなら Typesafe Platform で・Scala に Rails 必要?Rails と同じくらい楽に実装できて、かつ型があれば相当嬉しいはずと思っている・認証 API デザインとか手伝ってくれる人募集中・私が仕事に投入したら API を fix して 1.0 にする予定(おそらく遅くとも今年度中)・Better Java としての Scala にはまだまだ可能性ある

70

Page 71: めんどくさくない Scala #kwkni_scala

気に入ったら今すぐにhttp://git.io/skinny へ行ってstar を押してください!!!

スター乞食

71

Page 72: めんどくさくない Scala #kwkni_scala

宣伝のまとめ

・ScalikeJDBC は現在 1.6.10、業務利用実績多数で十分に stable、production ready です・ScalikeJDBC-Async は現在 0.2.8、まだ α version で本番実績は少ない、人柱上等な人が使ってください・Skinny Framework は 0.9.4、おおまかなアーキテクチャはほぼ固まったが、内部 API 改善と機能追加の真っ最中、2013 4Q までには 1.0 を出したいと思ってます

72

Page 73: めんどくさくない Scala #kwkni_scala

ぼっちはイヤだ!!!一緒に捗る Scala をつくりましょう!

73