From Ruby to Scala
Upcoming SlideShare
Loading in...5
×
 

From Ruby to Scala

on

  • 1,018 views

Introduction of Ruby and Scala

Introduction of Ruby and Scala

Statistics

Views

Total Views
1,018
Views on SlideShare
176
Embed Views
842

Actions

Likes
0
Downloads
4
Comments
0

4 Embeds 842

http://www.todesking.com 849
http://feedly.com 6
http://localhost 4
https://www.inoreader.com 1

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • はい、というわけでね、プレゼン始めさせてもらおうと思うんですけど
  • まず最初に質問なんですが、この会場にRubyを使ったことある方どれくらいいらっしゃいますか? <br /> フムー <br /> 本日はRubyの話をしに来ました
  • というわけでFrom Ruby to Scalaというタイトルで発表させていただきます。戦争が起きそうなタイトルですけど、RubyはクソだからScalaに行こうぜってわけじゃなくて私がRubyからScalaに行った時の体験談についての話をします!
  • はじめまして皆さん。TwitterやGitHubではtodeskingという名前でやってます。
  • 今の仕事はMaverickという会社で、Scalaで広告システムを作ってます。 <br /> ちなみにMaverickというのはScalaMatsuriのプラチナスポンサーです!!
  • まず私のバックグラウンドについてお話します。 <br /> 最初はSIerでJavaやってました。推薦エンジンとか、バッチを書くのがメイン。 <br /> 次の会社ではRuby/Railsでソーシャルブラウザゲームのサーバサイドを担当してました。 <br /> 今の会社ではScalaで広告システムを作ってます。
  • DSPというのがどういうものかについてはぐぐってください。 <br /> 簡単に言うとWebの広告配信を代行するサービスです。クライアントから広告を集めて、SSPと呼ばれるサービスの広告枠オークションを通じて広告を配信します。 <br /> オークションは誰かが広告枠のあるページを閲覧するたびに行われるため、膨大なリクエストを数十msの遅延で返す必要があります。その制限の中で、広告のCVRを上げるために機械学習など使ったインテリジェントな判断をする必要があるのでむずかしいですね。 <br /> まあ今日はそういう話しないんですけどね。
  • 本題に入る前に、今日話すことについて説明します。 <br /> まずScalaでハイパフォーマンスシステムを作る方法については触れません。これは一言で言うと「正しいことをやり、それを続ける」ことで実現可能です。遅い処理を書かない、ボトルネックを見つけて最適化する、まずい設計は直す。それを続けるだけです。以上。 <br /> みんな大好きなコンパイル時間についての話もしません。速いマシンを買うと改善するという噂があります!! <br /> RubyとScalaはどちらが良いのかについても話しません。最近は動的型付けと静的型付けの対立を煽るような動きがありますが、それぞれの言語には独自のいいところがあるので各位仲良くしましょう!!
  • さて、私はこの4月にRubyからScalaに乗り換えたわけですけど、移行は思ったよりスムーズにいきました。 <br /> RubyとScalaは、かなり似た特徴を持っています。 <br /> どちらもOOベースで関数型の機能を取り入れていること。そして、生産性と楽しさを重視していること。
  • まず、どちらもクラスベースのオブジェクト指向言語です。それぞれの言語でほぼ同じ内容のクラスを定義してみました。どちらの言語も、簡潔な記法でクラスを定義するための機能を備えています。
  • また、関数型の機能を取り入れており、無名の関数をインラインで作ったり関数を値として扱うための機構を備えています。
  • そして、一番の共通点は「楽しさ」をコンセプトの一つにしていることです。Rubyの作者であるMatzの”Rubyをキメると気持ちいい"という名言は有名ですし、Martin Odersky氏もScalaの特徴としてScalable, Functionalなどの特徴と並んでFunを挙げています。
  • 先ほど紹介したように、RubyとScalaは似た方向性を持つ言語です。 <br /> しかし、その目的を実現するための方法は二つの言語で大きく違います。 <br /> 今日は、この二つの言語がどのようなアプローチで問題を解決しているのかを紹介しようと思います。
  • これは先ほども出した、Personクラスの定義です。 <br /> Scalaの定義は、まあ見たままですね。この一行でnameとageという二つのメンバを持つPersonクラスを定義しなさいという、そういう文です。 <br /> さて、Rubyにおいてクラス定義がどのようにされているのか見てみましょう。
  • クラス定義はclassというキーワードから始めるのはRubyと同様。 <br /> 小なり記号は、このクラスが右のクラスを継承することを表します。 <br /> 余談ですが、Scalaでもextendsとかwithとかやめて記号で書きたいですねー。 <br /> というわけで、ここではStructをnewした結果を継承したPersonクラスを定義していますね。 <br /> なんだって??? <br /> 何故かクラス定義時に新しいインスタンスを作っているんですね。 <br /> この例は複雑なので、まずは簡単な例から見てみます。
  • 先ほどと同じクラス定義を、なるべく基本機能のみで書いてみました。 <br /> defキーワードでメソッド定義されるのはScalaと同様。initializeという名前のメソッドはコンストラクタになります。 <br /> @のついた識別子はインスタンス変数を表します。コンストラクタでnameとageを受け取り、同名のインスタンス変数にセットしています。 <br /> その下に並んでいるのはインスタンス変数に対するゲッタとセッタメソッドの定義です。 <br /> この単純なやりかただと、まるでジャバのように大量のボイラープレートコードを書く必要があってだるいですね。
  • そこでアクセサメソッドの定義をもっと単純にしたいと思います。 <br /> 先ほどのメソッド定義の代わりに、attr_readerキーワードを使います。 <br /> これを使用することで、指定した名前のセッタとゲッタが自動生成されます。 <br /> 他にもゲッタのみを生成するattr_reader、セッタのみを生成するattr_writerも用意されています。 <br /> さて、今キーワードと言ったけど、それは嘘でした。ここでやっているのは、単なるattr_accessorというメソッドの呼び出しです。
  • クラス定義内に書いた式はScalaだとコンストラクタ扱いになりますが、Rubyの場合はそのクラス定義が読まれたタイミングで評価されます。今の例だと、自分自身、つまりPersonクラスに対してattr_accessorメソッドを呼びます。 <br /> このメソッドが呼ばれると、ゲッタとセッタ二つのメソッドが自動的に生成されます。ほぼ同じ処理を手動で書くと、define_methodを呼び出すことでこのように書けます。
  • これがRubyの特徴的なところで、この言語においては全てがオブジェクトとして表現されています。全てというのは、つまり、クラスも含みます。 <br /> 定数AにClassクラスの新しいインスタンスを代入することで新しいクラスが作れるし、クラスAに対してdefine_methodメソッドを呼ぶことで新しいメソッドを定義できます。
  • このattr_accesorメソッドのような、クラス定義内で使えるメソッドのことをRubyでは「マクロ」と呼んだりします。 <br /> Scala等のコンパイル言語でマクロと言うと、コンパイル時に評価されてASTいじったりする機能のことを指すのでここはRuby文化の特殊なところですね。そもそもRubyにはコンパイル時という概念がないので、他の言語だとマクロで実現するような機能をマクロって読んでるんじゃないかと思います。
  • Scalaでアクセサメソッドを定義しようと思ったら、valやvarキーワードという言語組み込みの機能を使うことになります。これって頑張ればマクロで実現可能なのかというと、たぶん無理な気がします <br /> 必要な機能セットをあらかじめsyntaxに含める必要があるScalaと、クラスを動的に操作可能にすることで単なるメソッドで実現可能にするRubyという対比ができそうですね。
  • 最初の例に戻りましょう。Rubyにおいてはクラス定義も動的に実行されるため、Personの定義時にStructのインスタンスが作成され、継承元として指定されます。
  • アクセサの例ではメソッドを動的に定義していましたが、Rubyにおいてはクラスそのものも同様に動的生成できます。 <br /> Structにアクセサ名を渡してnewすると、先ほどの単純な例で示したのと同じようなクラスが自動生成されます。 <br /> このクラスを継承することで、適切なコンストラクタとアクセサを使えるようになってメデタシメデタシとなります。
  • というわけで、クラス定義を例にしてRubyが動的にクラスを操作可能な言語だということを見てきました。
  • OOの話をしたので次に関数について比べてみます。 <br /> ScalaとRubyはどちらも関数を値として扱ったり、インラインで関数を作るための機能を持っています。 <br /> 変数に束縛した関数を呼ぶ場合、Scalaの場合は通常の関数と同じように呼べますがRubyの場合は関数と値が区別されていて、callメソッドまたはそのエイリアスを呼ぶ必要があります。ドットの後に括弧書く記法は最近のバージョンで入ったみたいなんですが、なんかすごいですねこれ……
  • 関数を値として扱うときの方針は、関数型言語の中でも判断がわかれています。たとえばSchemeやJavaScriptのような言語だと、関数名を書けばその関数を値として取得できて、引数を渡せばその関数を呼べるというシンプルで一貫性のある仕様になっています。いっぽうCommonLispだと関数と値の名前空間が分かれていて、関数を値にしたい、あるいは値を関数として呼びたいときは特殊な記法を使う必要があります。 <br /> 一般的に、利便性を重視しようとするとルールを複雑にする必要が出てくるようです。Scalaでも、名前空間こそ同じですが関数名書いただけだと値として扱ってくれずに_つける必要があったり、このへんは工夫のしどころみたいです。
  • 関数を値として取得する方法についてもう少し詳しく見てみます。 <br /> まずはScalaの事例。fooというIntをとってStringを返す1引数の関数について考えてみます。 <br /> Scalaにおいては関数と値の名前空間は同一ですが、f = fooのように、直接関数名を書いてもScalaは値として扱ってくれません。 <br /> foo _ のように、識別子の後ろにアンスコをつけることで「その関数を値として取得する」という指定ができます。 <br /> また、別の関数にfooを渡す場合については、名前を指定するだけでもいけます。 <br /> 匿名関数を作る場合は、このようにファットアローで書きます。Scalaの関数呼び出しにおいては、1引数の場合丸括弧のかわりに波括弧も使えます。関数を渡す場合波括弧を使うのが慣習になっているようです。 <br /> また、アンダースコアを使うことで「第一引数のtoStringを呼んだ結果」を_.toStringと書けて便利というのもあります。
  • Rubyにおいては、関数を値として取得する場合はmethodメソッドに対象の名前を渡してやる必要があります。実際のコードではあまりこういうことはせず、対象を呼び出す匿名関数を作るケースのほうが一般的だと思いますが。 <br /> 関数を受け取る関数を書く場合、通常のオブジェクトとして受け取る方法とブロックとして受け取る方法があります。 <br /> ブロックというのは関数呼び出し時に一つだけ渡せるコードブロックのことで、呼び出しの後に波括弧でくくった内容が匿名関数として渡せます。値をブロックとして渡したいときは引数に&をつけます。
  • Rubyブロック引数の特徴として、関数以外の値も渡せることが挙げられます。 <br /> このコードは数値の配列を文字列の配列にマップしていますが、ここでmapの引数として渡している&:to_sというのはScalaで言うところの_.toString相当です。 <br /> RubyにおいてのコロンはScalaのシングルクオート相当で、Symbolリテラルになっています。 <br /> さて、シンボルをブロックとして与えると何が起こるんでしょう。実はRubyでは、ブロックとして渡した値に対してto_procメソッドを呼んで、その結果を使用します。Symbolのto_procは、「引数を一つとって、そのオブジェクトに対して自分と同名のメソッドを呼ぶ」という関数を返すように定義されているため、このようなことが可能になってます。ちょっとわかりにくいですが、:to_sというSymbolをto_procすると、引数のto_procを呼ぶような関数が生成されるということですね。
  • 次にコレクションについて見てみます。 <br /> Rubyの場合は連想配列と配列のリテラルがあります。 <br /> いっぽう、Scalaの場合はTupleのリテラルがあります。 <br /> 最初Scalaに入った時は、これが不思議でした。よく使うコレクションにリテラルが用意されてなくて、Seq()とかMap()とか書かなきゃいけないのはなんでだろうと。
  • ここでScalaの標準コレクションを見てみましょう。値を列挙できるTraversable型が元になって、不変と可変それぞれに用途別の細分化されたデータ構造が準備されています。 <br /> さて、今度はRubyのコレクションを見てみましょう。 <br /> 対応関係はこんなかんじです。 <br /> あ、TupleはArrayで代用されます。 <br /> ScalaにTuple以外のコレクションリテラルがないのは、標準的に使われる唯一の配列型やハッシュ型というのがないせいなんでしょうね。
  • Rubyのライブラリ設計についてよく言われるのが、「大クラス主義」です。Arrayクラスはスタックのようなpush/pop操作、キューのようなshift/pop操作、あるいは任意の位置に要素を挿入したり、インデクスでアクセスしたり列挙したりできて、あと異なる型の値を入れてタプル的にも使われています。実際のところ、Ruby標準ライブラリにはスタックやリンクドリストというクラスはなくて、全部アレイで済ますことになっている。 <br /> 同様に連想配列についてもミュータブルなハッシュ実装がひとつあるだけで、キーとバリューの組み合わせが必要なところではどこでもこれが使われています。
  • RubyとScalaのコレクション設計で特筆すべきは、型変換の問題です。Rubyにおいては少数の大きいコレクションクラスしかないので、特に複雑なことはありません。たいていのmapメソッドはArrayを返します。
  • これがScalaになると話が違います。IteratorをmapするとIteratorになるし、ListをmapするとListが返ります。mapの返り値がコレクションごとに異なるというだけなら、がんばって各クラスごとに特殊化したmapを書いてもいいかもしれませんが、BitSetの中身をIntにマップすると返り値はBitSet、StringにマップするとなんとSortedSetになります。 <br /> Scalaにおいては、これらの処理をDRYに書きつつ静的に適切な型が選ばれるように、CanBuildFrom型のimplicit引数が使われています。 <br /> このかっこいいmapメソッドのシグネチャを御覧ください。静的に解決できることは極力静的に解決するために、型の力を最大限使おうというのが見て取れます。
  • さて、次に既存クラスの拡張についてです。 <br /> Scalaにおいては、implicit conversionであるクラスを拡張する別のクラスに変換してメソッドを追加したり、あるいは既存クラスの挙動をサブクラス化で変えるなどができます。
  • しかしRubyの場合は動的にクラスを変更できるので、Scalaよりかなり過激なことが出来ます。 <br /> できることの一例としては、既存クラスのメソッドを別のものに差し替えるとか、特異メソッドとかシングルトンメソッドと呼ばれる仕組みで特定のオブジェクトのみメソッドを上書きするということが可能です。 <br /> また、includeという機構を使うことでクラスの継承関係に新しいクラスを差し込んだりすることもできます。 <br /> 既存クラスをそんなダイナミックに書き換えてよく事故らないなという感じですけど、意外と大丈夫です。なぜか。特にRuby on Railsを使うと、文字列や数値といった標準型にも大量の便利メソッドが追加されたりしてダイナミックな感じです。 <br /> 既存クラスを書き換えることはモンキーパッチングなどと呼ばれています。ライブラリにバグが出る場合にメソッド書き換えて逃げるとか、テスト時だけダミーのメソッドに差し替えるとかできるのではまるとかなり便利です。 <br /> あと、refinementsという特定のスコープだけメソッドの書き換えを有効にする仕組みが最近入りました。
  • 最後に、値が存在しないという事象をどう扱うかについて説明します。Scalaの場合はOptionですね。Rubyの場合はみんな大好きヌルを使います。Rubyにおいてはニルという呼び方なんですが、この値はfalseと合わせて、if文においては偽と判定されます。なのでobjがnilでないときだけメソッドを呼ぶにはif文の中にobjを入れるだけなので簡単ですね。 <br /> こういうパターンはあまりによく使うので、Railsで使われてる拡張ライブラリであるActiveSupportではtryメソッドが導入されました。呼びたいメソッド名を渡すことで、nilの場合は何もせず、そうじゃなかったらメソッドを実際に呼ぶというやつです。Rubyではこんなかんじなので、nilに対してメソッドを呼んでNoMethodErrorになる事例が頻発してます。まあ動的言語だと色いろあるよね…… 安全性はともかく、Scalaよりシンプルに書けるというメリットはあります。
  • というわけで、Scalaとの比較を通してRuby言語の機能を紹介していきました。 <br /> なんでも動的に解決するRubyとコンパイル時にすごく色々やるScala、やり方はぜんぜん違うんですけど、生産性を高めて楽しくプログラミングしようというところは似てると思います!! <br /> 今日見てきたように、ある問題を解決するための方法は複数あって、どれを選択するかが言語の個性になっています。他の言語を学ぶことによって色々な方法を知るのはおもしろいのでいろんな言語をやりましょう。

From Ruby to Scala From Ruby to Scala Presentation Transcript

  • TEST
  • Hello, is there Rubyist?
  • (When I moved) From Ruby to Scala @todesking https://www.flickr.com/photos/7armyjmtc/14249465709
  • Who: @todesking uses twitter and GitHub web: todesking.com
  • Working at Platinum Sponsor
  • My background • Sistem Integrator(2008-2012) • Buisiness batches • Recommendation system • With Java • Social game servicer • Browser based social game server • With Rails • DSP servicer • Adverting technology(Real-time bidding, user trakcing, machine learning) • With Scala • Vim
  • What is DSP Ad Clients DSP SSP I'm here Pages real time bidding(RTB) Web • "Demand Side Platform" • You can find detailed description in at Internet.
  • Today I NOT talk to: • How to build high-performance system • DO THE RIGHT THING and KEEP GOING, thats all. • How to speed up compilation time • BUY FASTER MACHINE • Whitch is greatest language • Every language has own good things • BE PEACEFUL
  • I moved from Ruby to Scala, in this April • That is smoother than I thought. • Basically, Ruby and Scala has common attributes • object-oriented and functional paradigm • Modern features for productivity and fun
  • Ruby and Scala is Class-based Object Oriented language • Class based object oriented language class Person(val name:String, val age:Option[Int]) { def hello():Unit = { class Person < Struct.new(:name, :age) def hello puts "Hello there, my name is #{name}." end end println(s"Hello there, my name is ${name}.") } }
  • At first: Common attributes of Ruby and Scala • Class based object oriented language with functional features def sumOfAge(people:Seq[Person]):Int = { people.map(_.age).flatten.foldLeft(0)(_ + _) } def sum_of_age(people) people.map(&:age).compact.reduce(0, &:+) end
  • At first: Common attributes of Ruby and Scala • "Fun" language • "Rubyをキメると気持ちいい"(Matz) • I can't translate it... •  →  • "Maybe most important is that programming in Scala tends to be very enjoyable."
  • Today's theme: How these languages do things in different way
  • Class definition class Person(val name:String, val age:Option[Int]) val person = new Person("tode sking", Some(31)) class Person < Struct.new(:name, :age) end person = Person.new("tode sking", 31)
  • Class definition in Ruby "class" for class definition class Person < Struct.new(:name, :age) "<" means "extends" Create new instance of Struct WHAT?! Creating new instance at class definition?!?!
  • Let's begin most plain case class Person def initialize(name, age) @name = name @age = age end def name; @name; end def name=(val); @name = val; end def age; @age; end def age=(val); @age = val; end end "def" for method definition method named "initialize" is treated as ctor "@name" is instance variable getter and setter methods Lot of boilerplate
  • Auto generating accessor method in Ruby class Person def initialize(name, age) ... attr_reader :name attr_reader :age end "attr_reader" is keyword for generating accessor method "attr_writer" and "attr_reader" is also available NO, actually it is not KEYWORD It just a METHOD.
  • Auto generating accessor method in Ruby attr_accessor :name ≒ define_method("name") { @name } define_method("name=") {|val| @name = val } ≒ def name; @name; end def mane=(val); @name = val; end
  • Ruby is DYNAMIC language • EVERYTHING is object • Class is object too • o = Object.new; o.class => Object • If you want to create new class, try A = Class.new • (assign constant A to fresh class object) • To define a method to A, try A.define_method(:foo){...} • (actually, define_method is "private". You should use A.instance_eval { define_method(:foo) ... })
  • It called "Macro" in Ruby • Such method called "macro"(Actually, it is just a method!!) • Ruby has no "compile-time". all execution is in "runtime"
  • Defining accessor method: Ruby and Scala • scala • var foo • roughly equal to { private[this] var foo_; def foo = foo_ } • specialized syntax for accessor • Ruby • attr_* • Enhance class definition with DSLs
  • Auto generating accessor method in Ruby class Person < Struct.new(:name, :age) Create a new instance of Struct Struct is a kind of Class ≒ PersonBase = Struct.new(:name, :age) class Person < PersonBase
  • Auto generating accessor method in Ruby Struct.new(:name, age) returns class that like class (unnamed) def initialize(name, age) @name, @age = name, age end attr_accessor :name attr_accessor :age # other utility methods... end[
  • Class definition class Person(val name:String, val age:Option[Int]) val person = new Person("tode sking", Some(31)) class Person < Struct.new(:name, :age) end person = Person.new("tode sking", 31)
  • Function as value • Scala: • val f:Int => Int = _ + 1 • f(1) //=> 2 • val g:Any => String = _.toString • Ruby • f = ->(x){x + 1} • f.call(1) or f[1] or f.(1) • mehod and value is strongly separated • g = ->(x){x.to_s}
  • Functions as value in other languages • Scheme: f, (f a b) • javascript: f, f(a, b) • Common Lisp: #'f, (f a b), (funcall #'f (list arg1 arg2))
  • Use function as value: Scala case • def foo(x:Int):String • val f = foo // not works • val f = foo _ // works • def bar(f:Int => String) • bar(foo) // works • bar(x => x.toString) • bar { x => x.toString } • bar(_.toString)
  • Use function as value: Ruby case • def foo(x) • f = method(:foo) • f = ->(x){ foo(x) } • def bar1(f) • def bar2(&f) • bar1(f) • bar2 {|x| foo(x) } • bar2(&f)
  • Use any object as function • [1, 2, 3].map(&:to_s) #=> ["1", "2", "3"] • map(&:to_s) • It behave like Scala's map(_.toString) • If block value is given, Ruby runtime call its "to_proc" method and use the result value as block. • Ruby's Symbol class has to_proc method • :to_s.to_proc #=> {|obj| obj.to_s }
  • Collections • Literals • Ruby: {}, [] • Scala: () • Neither list nor hash literals
  • Collections Map HashMap TupleN LinkedHashMa p HashMap Seq immutabl e LinkedHashMa p List Stack mutable Buffer Traversabl e Enumerable Hash {key => value} Array [1, 2, 3] (a, b)
  • Ruby has "Big" classes • "Array" is generic, mutable data structure, it can, push/pop, shift/pop, insert, indexed and sequential access, and also used as tuple • "Hash" is generic, mutable (and only) key-vakue map structure in Ruby standard library
  • Transform collection in Big-class style • [1, 2, 3].map(&:to_s) #=> ["1", "2", "3"] • Ruby has few Big collection classes. Everything goes simple.
  • Transform collection in Scala • Iterator[A].map(f:A => B):Iterator[B] • List[A].map(f:A => B):List[B] • BitSet.map(f:Int => Int):BitSet • BitSet.map(f:Int => String):SortedSet[String] • Magic "CanBuildFrom" implicit argument is used to solve this problem. • def map[B, That](f: (Int) ⇒ B)(implicit bf: CanBuildFrom[A, B, That]): That • Scala used types VERY MUCH.It's cool.
  • How to ehnahce existing class in Scala • implicit conversion • subclassing
  • How to ehnahce existing class in Ruby • subclassing • direct modification(simplly overwrite/alias_method_chain) • define singleton method • include/extend • refinements
  • How to handle "non-existens value" • Scala: Option[T] • Ruby: ♥nil♥ • false and nil is "falsy". Other values are "truthy". • obj.foo if obj # call obj.foo if obj is not nil • obj.try(:foo) # same (ActiveSupport feature)
  • Conclusion • Ruby and Scala is very different, but they share same goal: be productive, be fun. • There are many different approach to solve problems.We can learn things and achieve fresh surprise from other culture. Have a fun with various programming!!
  • Today I want talk to: • Libraries • rubygems: High accessibility • Source distribution • Easy to publish • maven repo: Top on Java ecosystem • Some impedance missmatch • Binary distribution • Bit difficult to publish, especially official repo. • Build/Task system • Rake: Simple scripting with internal DSL • Sbt: Type-safe and immutable dependency graph construction with cool := ++= <<= s • It's Scala's way!! • Community • Ruby: Humanity • Love emotional context • Scala: Types • Love Monads !!!!