[email protected] (@marius)
[translated by Yuta Okamoto (@okapies) and Satoshi Kobayashi (@scova0731)] .TOC English Ð ÑÑÑкОй ç®äœäžæ ## åºç« [Scala][Scala] 㯠Twitter ã§äœ¿ãããŠããäž»èŠãªã¢ããªã±ãŒã·ã§ã³ããã°ã©ãã³ã°èšèªã®äžã€ã ãTwitter ã®ã€ã³ãã©ã®å€§éšå㯠Scala ã§æžãããŠããããæã ã®æ¥åãæ¯ãã[倧èŠæš¡ã©ã€ãã©ãª](http://github.com/twitter/)ãããã€ãæã£ãŠãããScala ã¯æ¥µããŠå¹æçã ããäžæ¹ã§å·šå€§ãªèšèªã§ããããæã ã®çµéšã§ã¯ãScala ã®é©çšã«ã¯ååãªæ³šæãå¿ èŠã ãèœãšãç©Žã¯äœãïŒ ã©ã®æ©èœã掻çšããŠãã©ããæ§ããã¹ããïŒ ãã€"çŽç²é¢æ°åã®ã¹ã¿ã€ã«"ãæ¡çšããŠããã€é¿ããã¹ããïŒ èšãæãããªããæã ãèŠåºãã"Scala ã®å¹æç (effective) ãªäœ¿ãæ¹"ãšã¯äœãïŒ æ¬ã¬ã€ãã®ç®çã¯ãæã ã®çµéšããæãåºãããäžé£ã®*ãã¹ããã©ã¯ãã£ã¹*ãæäŸããããšã ãTwitter ã§ã¯ã䞻㫠Scala ãåæ£ã·ã¹ãã ãæ§æãã倧容éãµãŒãã¹çŸ€ã®äœæã«äœ¿ã£ãŠããã®ã§ãæã ã®å©èšã«ã¯ãã€ã¢ã¹ãããã£ãŠãããããããããã«ããã¢ããã€ã¹ã®å€§åã¯ãä»ã®åé¡é åãžèªç¶ã«ç§»ãæ¿ããããã¯ãã ãããã¯èŠåã§ã¯ãªããã ããéžè±ã¯æ£åœåãããã¹ãã ã Scala ãæäŸããããããã®éå ·ã¯ãç°¡æœãªè¡šçŸãå¯èœã«ãããã¿ã€ãã³ã°ãæžãã°èªãéãæžãã倧æµã¯èªãéãæžãã°ããéãèªãããæ ã«ãç°¡æœã§ããã»ã©ããæå¿«ã«ãªããããããŸããç°¡æœãã¯æ£å察ã®å¹æãããããâãªãŸããéå ·âãšããªããã: æ£ç¢ºãã®æ¬¡ã«ã¯ããã€ãèªã¿æã®ããšãèãããã äœããã *Scala ã§ããã°ã©ã ããã*ãåãæžããŠããã®ã¯ Java ã§ã¯ãªãããHaskell ã§ããPython ã§ããªããScala ã®ããã°ã©ã ã¯ããããã®èšèªã§æžããããã®ãšãç°ãªã£ãŠãããããã°ã©ãã³ã°èšèªãå¹æçã«äœ¿ãã«ã¯ãåã®åé¡ããã®èšèªã®çšèªã§è¡šçŸããã¹ãã ãJava ã®ããã°ã©ã ãç¡çç¢ç Scala ã§è¡šçŸããŠããã»ãšãã©ã®å Žåãªãªãžãã«ããå£ã£ããã®ã«ãªãã ããã ãã㯠Scala ã®å ¥éã§ã¯ãªããæ¬ã¬ã€ã㯠Scala ã«æ £ã芪ããã èªè ãåæãšããŠããããããã Scala ãåŠã³ããèªè ã«ã¯ä»¥äžã®ãããªææããã: * [Scala School](https://twitter.github.com/scala_school/) * [Learning Scala](https://www.scala-lang.org/node/1305) * [Learning Scala in Small Bites](https://matt.might.net/articles/learning-scala-in-small-bites/) æ¬ã¬ã€ãã¯çããããã¥ã¡ã³ããªã®ã§ãæã ã®ææ°ã®âãã¹ããã©ã¯ãã£ã¹âãåæ ããããã«å€æŽããããããããªããããããäžæ žãšãªãã¢ã€ãã¢ãå€ããããšã¯ãªãã ãã: å¯èªæ§ãåžžã«åªå ãã; æ±çšçãªã³ãŒããæžãããããæçããæãªããªãããš; ã·ã³ãã«ãªèšèªæ©èœã掻çšãããã·ã³ãã«ãã¯å倧ãªåãããããããŸãïŒç¹ã«åã·ã¹ãã ã«ãããïŒé£è§£ããåé¿ã§ããããšããããåžžã«ãã¬ãŒããªããæèããããæŽç·Žãããèšèªã¯è€éãªå®è£ ãèŠæ±ããè€éãã¯è€éããçããæšè«ã®è€éããæå³è«ã®è€éããæ©èœéçžäºäœçšã®è€éãããããŠåã®ååè ãžã®ç解ã®è€éããããããã£ãŠãè€éãã¯æŽç·Žãããããçšéã§ãããå¹çšãã³ã¹ããäžåã£ãŠããããšãåžžã«ç¢ºèªãã¹ãã ã ã§ã¯ã楜ããã§ã»ããã ## æžåŒ ã³ãŒãã®*æžåŒ*ã®çŽ°ãã話ã¯ïŒãããå®è·µçã§ããéãã¯ïŒéèŠã§ã¯ãªããåœç¶ã ããã¹ã¿ã€ã«ã«æ¬è³ªçãªè¯ãæªãã¯ãªãããå人çãªå¥œã¿ã¯ã»ãŒäººã«ãã£ãŠç°ãªããããããåãæŽåœ¢ã«ãŒã«ã*äžè²«ããŠ*é©çšããã°ãã»ãšãã©ã®å Žåã§å¯èªæ§ãé«ãŸããç¹å®ã®ã¹ã¿ã€ã«ã«éŠŽæãã èªã¿æã¯ãããã«å¥ã®ããŒã«ã«ãªæ £ç¿ãææ¡ããããèšèªã®ææ³ã®é ã解èªãããããå¿ èŠããªãã ããã¯ãéè€åºŠã®é«ãææ³ãæ〠Scala ã«ãããŠã¯ç¹ã«éèŠã ãã¡ãœããåŒã³åºãã¯åãããããäŸã : ã¡ãœãã㯠"`.`" ãä»ããŠãããã¯ã€ãã¹ããŒã¹ã§ç©ºããŠãåŒã³åºãããåæ§ã«ããŒããŸãã¯äžã€åŒæ°ãåãã¡ãœããã§ã¯ãäžžã«ãã³ãä»ããŠãè¯ããä»ããªããŠãè¯ãããšãã£ãæ§ã«ãããã«ãã¡ãœããåŒã³åºãã®æ§ã ãªã¹ã¿ã€ã«ã¯ãææ³äžã®æ§ã ãªææ§ãããããåºãïŒ æ³šææ·±ãéžã°ããæŽåœ¢ã«ãŒã«ãäžè²«ããŠé©çšããããšã§ã人éãšæ©æ¢°ã®äž¡æ¹ã«ãšã£ãŠã®å€ãã®ææ§ãã解決ã§ããã®ã¯ééããªãã æã ã¯ã[Scala style guide](http://docs.scala-lang.org/style/) ãé å®ãããšå ±ã«ä»¥äžã®ã«ãŒã«ãè¿œå ããã ### ãã¯ã€ãã¹ããŒã¹ ã€ã³ãã³ãã¯ãã¹ããŒã¹ 2 ã€ãšããã100 ã«ã©ã ãè¶ ããè¡ã¯é¿ãããã¡ãœãããã¯ã©ã¹ããªããžã§ã¯ãã®å®çŸ©å士ã®éã«äžè¡ç©ºããã ### åœå
- å°ããã¹ã³ãŒãã§ã¯çãååã䜿ã
- ã«ãŒãå
ã§ã¯
i
,j
,k
ãæåŸ ãããã - ãã倧ããã¹ã³ãŒãã§ã¯ãããé·ãååã䜿ã
- å€éšAPIã¯ãããé·ãã説æçã§æå³ä»ããããååãæã€ã¹ãã ãäŸãã°ã
Future.all
ã§ã¯ãªãFuture.collect
ãšããæ¹ãããã - äžè¬çãªç¥èªã䜿ããé£è§£ãªç¥èªãé¿ãã
ok
ãerr
ãdefn
ã¯èª°ããç¥ã£ãŠããããããsfri
ã¯ããã»ã©äžè¬çã§ã¯ãªãã- ç°ãªãçšéã«ååãåå©çšããªã
val
ã䜿ããã- äºçŽåã
`
ã§ãªãŒããŒããŒãããã®ã¯é¿ãã `type`
ã§ã¯ãªãtyp
ãšããã- å¯äœçšãèµ·ããæäœã¯èœåæ ã§åœåãã
user.setActive()
ã§ã¯ãªãuser.activate()
ãšããã- å€ãè¿ãã¡ãœããã¯èª¬æçã«åœåãã
src.defined
ã§ã¯ãªãsrc.isDefined
ãšããã- ã²ãã¿ãŒ (getter) ã®ååã®å
é ã«
get
ãä»ããªã - 以åã®ã«ãŒã«ãšåæ§ã«ããã¯åé·ã ã
site.getCount
ã§ã¯ãªãsite.count
ãšããã - ããã±ãŒãžåããªããžã§ã¯ãåã§æ¢ã«ã«ãã»ã«åãããŠããååãç¹°ãè¿ããªã
- ã§ã¯ãªã
object User { def getUser(id: Int): Option[User] }
ãšãããobject User { def get(id: Int): Option[User] }
User.getUser
ã¯åé·ã ããUser.get
ãããå€ãã®æ å ±ãäžããªãã
- import è¡ã¯ã¢ã«ãã¡ãããé ã«äžŠã¹æ¿ãã
- ãããããšèŠèŠçã«èª¿ã¹ããããèªååãããããã
- ããã±ãŒãžããè€æ°ã®ååãã€ã³ããŒãããéã¯äžã«ãã³ã䜿ã
import com.twitter.concurrent.{Broker, Offer}
- 6 å以äžã®ååãã€ã³ããŒãããéã¯ã¯ã€ã«ãã«ãŒãã䜿ã
- äŸ:
import com.twitter.concurrent._
ã¯ã€ã«ãã«ãŒããæ¿«çšããªãããšãäžéšã®ããã±ãŒãžã¯å€§éã®ååããšã¯ã¹ããŒãããã - ã³ã¬ã¯ã·ã§ã³ã䜿ãéã¯ã
scala.collection.immutable
ãšscala.collection.mutable
ã®äžæ¹ãããã¯äž¡æ¹ãã€ã³ããŒãããŠååã修食ãã - å¯å€ (mutable) ã³ã¬ã¯ã·ã§ã³ãšäžå€ (immutable) ã³ã¬ã¯ã·ã§ã³ã§ã¯ååãéè€ããŠãããååã修食ããŠãã©ã¡ãã®ã³ã¬ã¯ã·ã§ã³ã䜿ã£ãŠãããèªã¿æã«å¯ŸããŠæããã«ãããã (äŸ: "
immutable.Map
") - ä»ã®ããã±ãŒãžããã®çžå¯Ÿæå®ã§ã€ã³ããŒãããªã
- ãšã¯æžããã«ææ§ãã®ç¡ãæžãæ¹ããããã
import com.twitter import concurrent
import com.twitter.concurrent
- import æã¯ãã¡ã€ã«ã®å é ã«çœ®ã
- å šãŠã® import ãèªã¿æãäžç®æã§åç §ã§ããããã«ãããã
Service
ãšããæ»ãåãæããªãå Žåãã³ã³ãã€ã©ã¯çŽ°å¥å (refinement type) ã® Object with Service{def getId: Int}
ãçæããã代ããã«ãæ瀺çãªã¢ãããŒã·ã§ã³ã䜿ããš:
def make(): Service = new Service{}
`make` ã®å
¬éããåãå€æŽããããšãªãããã¬ã€ããããã«å¥œããªã ãããã¯ã¹ã§ãããã€ãŸããåŸæ¹äºææ§ã®ç®¡çã容æã«ãªãã
### å€äœ
å€äœ (variance) ã¯ããžã§ããªã¯ã¹ã掟çå (subtyping) ãšçµã³ã€ãéã«çŸãããå€äœã¯ãã³ã³ããå (*container* type) ã®æŽŸçåãšãèŠçŽ å (*contained* type) ã®æŽŸçåãã©ãé¢é£ããããå®çŸ©ãããScala ã§ã¯å€äœã¢ãããŒã·ã§ã³ã宣èšã§ããã®ã§ãã³ã¬ã¯ã·ã§ã³ã«ä»£è¡šãããå
±éã©ã€ãã©ãªã®äœè
ã¯ãå€æ°ã®ã¢ãããŒã·ã§ã³ãæ±ãå¿
èŠããããå€äœã¢ãããŒã·ã§ã³ã¯å
±æã³ãŒãã®äœ¿ãåæã«ãšã£ãŠéèŠã ãã誀çšãããšå±éºãªãã®ã«ãªãããã
éå€ (invariant) ã¯é«åºŠã ããScala ã®åã·ã¹ãã ã«ãšã£ãŠå¿
é ã®ç¹åŸŽã§ããã掟çåã®é©çšãå©ããããã«åºãïŒãããŠæ£ããïŒäœ¿ãããã¹ãã ã
*äžå€ã³ã¬ã¯ã·ã§ã³ã¯å
±å€ (covariant) ã§ããã¹ãã *ãèŠçŽ åãåãåãã¡ãœããã¯ãã³ã¬ã¯ã·ã§ã³ãé©åã«"æ Œäžã"ãã¹ãã :
trait Collection[+T] {
def add[U >: T](other: U): Collection[U]
}
*å¯å€ã³ã¬ã¯ã·ã§ã³ã¯éå€ã§ããã¹ãã *ãå
±å€ã¯ãéåžžã¯å¯å€ã³ã¬ã¯ã·ã§ã³ã«ãããŠã¯ç¡å¹ã ããã®
trait HashSet[+T] {
def add[U >: T](item: U)
}
.LP ãšã以äžã®åéå±€ã«ã€ããŠèããŠã¿ãã:
trait Mammal
trait Dog extends Mammal
trait Cat extends Mammal
.LP ããããã«ç¬ (Dog) ã®ããã·ã¥ã»ããããããªãã
val dogs: HashSet[Dog]
.LP ãããåºä¹³é¡ (Mammal) ã®éåãšããŠæ±ã£ãããç« (Cat) ãè¿œå ãããã§ããã
val mammals: HashSet[Mammal] = dogs
mammals.add(new Cat{})
.LP ããã¯ãã¯ããç¬ã® HashSet ã§ã¯ãªãïŒ
### åãšã€ãªã¢ã¹
åãšã€ãªã¢ã¹ (type alias) ã䜿ããšã䟿å©ãªååãæäŸããããæå³ãæçã«ãããã§ãããããããäžç®çç¶ãªåããšã€ãªã¢ã¹ãã¹ãã§ã¯ãªãã
() => Int
.LP ã¯ãçããŠäžè¬çãªåã䜿ã£ãŠããã®ã§ã
type IntMaker = () => Int
IntMaker
.LP ãããæå³ãæçã ããããã
class ConcurrentPool[K, V] {
type Queue = ConcurrentLinkedQueue[V]
type Map = ConcurrentHashMap[K, Queue]
...
}
.LP ã¯ãææçéãç®çã§ç°¡æœããé«ãããå Žåã«æçšã ã
ãšã€ãªã¢ã¹ã䜿ããå Žåã¯ããµãã¯ã©ã¹ã«ããŠã¯ãããªãã
trait SocketFactory extends (SocketAddress => Socket)
.LP SocketFactory
㯠Socket
ãçæããé¢æ°ã ããåãšã€ãªã¢ã¹
type SocketFactory = SocketAddress => Socket
.LP ã䜿ãæ¹ããããããã§ãSocketFactory
åã®å€ãšãªãé¢æ°ãªãã©ã«ãæäŸãããã®ã§ãé¢æ°åæã䜿ãã:
val addrToInet: SocketAddress => Long
val inetToSocket: Long => Socket
val factory: SocketFactory = addrToInet andThen inetToSocket
ããã±ãŒãžãªããžã§ã¯ãã䜿ããšãåãšã€ãªã¢ã¹ããããã¬ãã«åãšããŠæçžã§ãã:
package com.twitter
package object net {
type SocketFactory = (SocketAddress) => Socket
}
åãšã€ãªã¢ã¹ã¯æ°ããåã§ã¯ãªãããšã«æ³šæããããåãšã€ãªã¢ã¹ã¯ããšã€ãªã¢ã¹ãããååããã®åãžãšæ§æçã«çœ®æããããšãšåçã ã
### æé»
æé» (implicit) ã¯åŒ·åãªåã·ã¹ãã ã®æ©èœã ããæ
éã«äœ¿ãã¹ãã ããããã®è§£æ±ºã«ãŒã«ã¯è€éã§ãã·ã³ãã«ãªåå¥æ€æ»ã«ãããŠãããå®éã«äœãèµ·ããŠãããææ¡ããã®ãå°é£ã«ãããæé»ãééããªã䜿ã£ãŠãããã®ã¯ä»¥äžã®ç¶æ³ã :
* Scala ã¹ã¿ã€ã«ã®ã³ã¬ã¯ã·ã§ã³ãæ¡åŒµããããè¿œå ããããããšã
* ãªããžã§ã¯ããé©å (adapt) ããããæ¡åŒµ (extend) ããããããšãïŒ"pimp my library" ãã¿ãŒã³ïŒ
* [å¶çŽãšããã³ã¹](http://www.ne.jp/asahi/hishidama/home/tech/scala/generics.html#h_generalized_type_constraints)ãæäŸããŠ*åå®å
šã匷å*ããããã«äœ¿ã
* åãšããã³ã¹ïŒåã¯ã©ã¹ïŒãæäŸãããã
* `Manifest`ïŒScala 2.10 以é㯠`TypeTag`ïŒã®ãã
æé»ã䜿ãããšããæã¯ãæé»ã䜿ããã«åãããšãéæããæ¹æ³ããªããåžžã«ç¢ºèªãããã
䌌éã£ãããŒã¿åå士ããèªåçã«å€æããããã«æé»ã䜿ãã®ã¯ããããïŒäŸãã°ããªã¹ããã¹ããªãŒã ã«å€æããçïŒãæ瀺çã«å€æããã¹ãã ããããã®åã¯ããããç°ãªã£ãåäœãããã®ã§ãèªã¿æã¯æé»ã®åå€æãåããŠããªãã泚æããªããŠã¯ãªããªããªãã
## ã³ã¬ã¯ã·ã§ã³
Scala ã®ã³ã¬ã¯ã·ã§ã³ã©ã€ãã©ãªã¯éåžžã«ç·ç§°ç (generic) ã§ãæ©èœãè±å¯ã§ã匷åã§ãçµã¿ç«ãŠããããã³ã¬ã¯ã·ã§ã³ã¯é«æ°Žæºã§ãããå€æ°ã®æäœãå
¬éããŠãããå€ãã®ã³ã¬ã¯ã·ã§ã³æäœãšå€æãç°¡æœãã€èªã¿ãããè¡šçŸã§ããããããããæ©èœãäžæ³šæã«é©çšãããšããã°ãã°æ£å察ã®çµæãæããå
šãŠã® Scala ããã°ã©ã㯠[collections design document](http://docs.scala-lang.org/ja/overviews/collections/introduction.html) ãèªãã¹ãã ããã®ããã¥ã¡ã³ãã¯ãScala ã®ã³ã¬ã¯ã·ã§ã³ã©ã€ãã©ãªã«å¯ŸããåªããæŽå¯ãšã¢ãããŒã·ã§ã³ããããããŠãããã
åžžã«ãèŠæ±ãæãã·ã³ãã«ã«æºããã³ã¬ã¯ã·ã§ã³ã䜿ããã
### éå±€
Scala ã®ã³ã¬ã¯ã·ã§ã³ã©ã€ãã©ãªã¯å·šå€§ã ã`Traversable[T]` ãåºåºãšããå
¥ãçµãã ç¶æ¿éå±€ã ãã§ãªããã»ãšãã©ã®ã³ã¬ã¯ã·ã§ã³ã« `immutable` çãš `mutable` çããããã©ããªã«è€éã§ãã以äžã®å³ã¯ `immutable` ãš `mutable` ã®åæ¹ã®éå±€ã«ãšã£ãŠéèŠãªåºå¥ãå«ãã§ããã
.cmd
pic2graph -format png >coll.png <Iterable[T] ã¯ã€ãã¬ãŒã (iterate) ã§ããã³ã¬ã¯ã·ã§ã³ã§ãiterator
(ãš foreach
) ã¡ãœãããæäŸãããSeq[T]
ã¯é åºä»ããããã³ã¬ã¯ã·ã§ã³ãSet[T]
ã¯æ°åŠçéåïŒèŠçŽ ãäžæãªé åºä»ãã®ãªãã³ã¬ã¯ã·ã§ã³ïŒããã㊠Map[T]
ã¯é åºä»ãã®ãªãé£æ³é
åã ã
### 䜿ã
*äžå€ (immutable) ã³ã¬ã¯ã·ã§ã³ã䜿ããã*äžå€ã³ã¬ã¯ã·ã§ã³ã¯ã»ãšãã©ã®ç¶æ³ã«é©çšã§ããããŸããäžå€ã³ã¬ã¯ã·ã§ã³ã¯åç
§ééãªã®ã§ããã©ã«ãã§ã¹ã¬ããã»ãŒããšãªããããã°ã©ã ã®ç解ã容æã«ãªãã
*æ瀺çã« `mutable` åå空éã䜿ããã*`scala.collections.mutable._` ã import ã㊠`Set` ãåç
§ããã®ã§ã¯ãªãã
import scala.collections.mutable
val set = mutable.Set()
.LP ãšããããšã§ãå¯å€çã® `Set` ã䜿ãããŠããããšãã¯ã£ããããã
*ã³ã¬ã¯ã·ã§ã³åã®ããã©ã«ãã³ã³ã¹ãã©ã¯ã¿ã䜿ããã*äŸãã°ãé åºä»ãã®ïŒãã€é£çµãªã¹ãã§ããå¿
èŠããªãïŒã·ãŒã±ã³ã¹ãå¿
èŠãªå Žåã¯ããã€ã§ã `Seq()` ã³ã³ã¹ãã©ã¯ã¿ã䜿ãã:
val seq = Seq(1, 2, 3)
val set = Set(1, 2, 3)
val map = Map(1 -> "one", 2 -> "two", 3 -> "three")
.LP ãã®ã¹ã¿ã€ã«ã§ã¯ãã³ã¬ã¯ã·ã§ã³ã®åäœãšãã®å®è£
ãåãåããããã®ã§ãã³ã¬ã¯ã·ã§ã³ã©ã€ãã©ãªã«å¯ŸããŠæãé©åãªå®è£
åã䜿ãããããšãã§ãããåãå¿
èŠãªã®ã¯ Map
ã§ãã£ãŠãå¿
ãããèµ€é»æšã§ã¯ãªããããã«ããããã®ããã©ã«ãã³ã³ã¹ãã©ã¯ã¿ã¯ããã°ãã°ç¹æ®åãããè¡šçŸãçšãããäŸãã°ãMap()
㯠3 ã€ã®ããŒãæã€ãããã«å¯ŸããŠããã£ãŒã«ãã 3 ã€æã€ãªããžã§ã¯ãïŒMap3
ã¯ã©ã¹ïŒã䜿ãã
以äžã®åœç¶ã®åž°çµãšããŠãã¡ãœãããã³ã³ã¹ãã©ã¯ã¿ã§ã¯ã*æãç·ç§°çãªã³ã¬ã¯ã·ã§ã³åãé©åã«åãåãã*ãèŠããã«ãéåžžã¯äžèšã® `Iterable`, `Seq`, `Set`ãããã㯠`Map` ã®ãã¡ã®ã©ããäžã€ã ãã·ãŒã±ã³ã¹ãå¿
èŠãªã¡ãœããã«ã¯ `List[T]` ã§ã¯ãªã `Seq[T]` ã䜿ããã
.LP ïŒæ³šæ: `scala.package` ãå®çŸ©ããããã©ã«ãã® `Traversable`ã`Iterable` ãš `Seq` 㯠`scala.collection` ããŒãžã§ã³ã ãããã«å¯ŸããŠã`Predef.scala` ãå®çŸ©ãã `Map` ãš `Set` 㯠`scala.collection.immutable` ããŒãžã§ã³ã ããããæå³ããã®ã¯ãäŸãã°ãããã©ã«ãã® `Seq` åã¯äžå€ãšå¯å€ã®*äž¡æ¹*ã«ãªãããšããããšã ããããã£ãŠãåã®ã¡ãœããã®åŒæ°ãäžå€ã³ã¬ã¯ã·ã§ã³ã«äŸåãããªãã `Traversable`ã`Iterable` ã `Seq` ã䜿ãå Žåãæ確ã«äžå€ããŒãžã§ã³ã require/import ããå¿
èŠãããããããªããã°ãã¡ãœããã«å¯å€ããŒãžã§ã³ãæž¡ããããããããªããïŒ
### ã¹ã¿ã€ã«
é¢æ°åããã°ã©ãã³ã°ã§ã¯ããã€ãã©ã€ã³åããäžå€ã³ã¬ã¯ã·ã§ã³ã®å€æã«ãã£ãŠãã³ã¬ã¯ã·ã§ã³ãæã¿ã®çµæãžãšæ圢ããããšãæšå¥šãããŠããããã®ææ³ã«ãããå€ãã®åé¡ããšãŠãç°¡æœã«è§£æ±ºã§ããããããã¯èªã¿æãå°æãããå¯èœæ§ãããããã€ãã©ã€ã³åããå€æã¯æã«äœè
ã®æå³ã®ç解ãå°é£ã«ããã®ã§ããã®å Žåãæé»çã«ç€ºãããäžéçµæãå
šãŠè¿œè·¡ãç¶ãããããªããäŸãã°ãæ§ã
ãªããã°ã©ãã³ã°èšèªã«å¯Ÿããæ祚ã§ãã (language, num votes) ã®ã·ãŒã±ã³ã¹ãéèšããŠã祚æ°ãæãå€ãèšèªããé çªã«è¡šç€ºããã³ãŒãã¯ä»¥äžã®ããã«æžãã:
val votes = Seq(("scala", 1), ("java", 4), ("scala", 10), ("scala", 1), ("python", 10))
val orderedVotes = votes
.groupBy(_._1)
.map { case (which, counts) =>
(which, counts.foldLeft(0)(_ + _._2))
}.toSeq
.sortBy(_._2)
.reverse
.LP ãã®ã³ãŒãã¯ç°¡æœã§ãã€æ£ãããããããã»ãšãã©ã®èªã¿æã¯äœè
ã®å
ã
ã®æå³ãææ¡ããã®ã«èŠåŽããã ãããäžéçµæãšãã©ã¡ãŒã¿ã«ååãä»ããæŠç¥ã¯ãå€ãã®å Žåã§äœè
ã®æå³ãæ確ã«ããã®ã«åœ¹ç«ã€:
val votesByLang = votes groupBy { case (lang, _) => lang }
val sumByLang = votesByLang map { case (lang, counts) =>
val countsOnly = counts map { case (_, count) => count }
(lang, countsOnly.sum)
}
val orderedVotes = sumByLang.toSeq
.sortBy { case (_, count) => count }
.reverse
.LP ãã®ã³ãŒãã§ã¯ãæœãããå€æãäžéå€ã®ååãšããŠãæäœãããããŒã¿æ§é ããã©ã¡ãŒã¿åãšããŠè¡šããŠãããããã«ããã以åãšåããããç°¡æœã§ããã ãã§ãªãããããã£ããæçãªè¡šçŸãšãªã£ãŠãããããåå空éã®æ±æãå¿é
ãªããåŒã {}
ã§ã°ã«ãŒãåãããšè¯ã:
val orderedVotes = {
val votesByLang = ...
...
}
### æ§èœ
é«æ°Žæºã³ã¬ã¯ã·ã§ã³ã©ã€ãã©ãªã¯ïŒé«æ°Žæºãªæ§ç¯ç©ãäžè¬çã«ããã§ããããã«ïŒæ§èœã®æšæž¬ãé£ãããã³ã³ãã¥ãŒã¿ã«çŽæ¥æ瀺ããâåœä»€åã¹ã¿ã€ã«âããé ãããã»ã©ãããã³ãŒãçãæ§èœã«äžãã圱é¿ãå³å¯ã«äºæž¬ããã®ã¯å°é£ã«ãªããäžæ¹ã§ãã³ãŒãã®æ£ç¢ºããå€æããã®ã¯æŠããŠå®¹æã ããèªã¿ããããé«ãŸããScala ã®å ŽåãJava ã©ã³ã¿ã€ã ãäºæ
ãããã«è€éã«ããŠãããScala ã¯ãŠãŒã¶ã«å¯ŸããŠãã¯ã·ã³ã° (boxing) ããã³ã¢ã³ãã¯ã·ã³ã° (unboxing) æäœãé èœããã®ã§ãæ§èœãã¡ã¢ãªäœ¿çšéã®é¢ã§é倧ãªããã«ãã£ã被ãããšãããã
äœã¬ãã«ã«ããã现éšã«æ³šç®ããåã«ãåã®çšéã«å¯ŸããŠã³ã¬ã¯ã·ã§ã³ã®äœ¿ãæ¹ãé©åãã©ãã確èªãããããŸããããŒã¿æ§é ã«äºæããªã挞è¿çãªè€éãããªãã確ãããããScala ã®ããŸããŸãªã³ã¬ã¯ã·ã§ã³ã®è€éãã«ã€ããŠã¯[ãã¡ã](http://docs.scala-lang.org/ja/overviews/collections/performance-characteristics.html)ã§è¿°ã¹ãããŠããã
æ§èœæé©åã®ç¬¬äžæ³åã¯ãåã®ã¢ããªã±ãŒã·ã§ã³ã*ãªã*é
ãã®ããç解ããããšã ãæé©åãå§ããåã«ãåã®ã¢ããªã±ãŒã·ã§ã³ããããã¡ã€ã«^[[Yourkit](http://yourkit.com)ã¯è¯ããããã¡ã€ã©ã ã]ããŠããŒã¿ãåãããæåã«æ³šç®ããã®ã¯ãåæ°ã®å€ãã«ãŒãã巚倧ãªããŒã¿æ§é ã ãæé©åãžã®é床ãªåãçµã¿ã¯ããããŠãç¡é§ãªåªåã«çµãããã¯ããŒã¹ã®âææå°æ©ãªæé©åã¯è«žæªã®æ ¹æºâãšããæ Œèšãæãåºããã
æ§èœãã¡ã¢ãªå¹çãèŠæ±ãããå Žé¢ã§ã¯ãå€ãã®å Žåã§äœã¬ãã«ã³ã¬ã¯ã·ã§ã³ã䜿ãã®ã劥åœã ã巚倧ãªã·ãŒã±ã³ã¹ã«ã¯ããªã¹ãããé
åã䜿ããïŒäžå€ã® `Vector` ã³ã¬ã¯ã·ã§ã³ã¯ãé
åãžã®åç
§ééãªã€ã³ã¿ãã§ãŒã¹ãæäŸããïŒããŸããæ§èœãéèŠãªå Žåã¯ãã·ãŒã±ã³ã¹ãçŽæ¥çæããã«ãããã¡ã䜿ããã
### Java ã³ã¬ã¯ã·ã§ã³
Java ã³ã¬ã¯ã·ã§ã³ãš Scala ã³ã¬ã¯ã·ã§ã³ãšçžäºéçšããã«ã¯ã`scala.collection.JavaConverters` ã䜿ããã`JavaConverters` ã¯ãæé»å€æãè¡ã `asJava` ã¡ãœãããš `asScala` ã¡ãœãããè¿œå ãããèªã¿æã®ããã«ããããã®å€æã¯æ瀺çã«è¡ãããã«ããã:
import scala.collection.JavaConverters._
val list: java.util.List[Int] = Seq(1,2,3,4).asJava
val buffer: scala.collection.mutable.Buffer[Int] = list.asScala
## 䞊è¡æ§
çŸä»£ã®ãµãŒãã¹ã¯é«ã䞊è¡æ§ (concurrency) ãåãããµãŒããäœäžäœåäžãã®åææäœãã³ãŒãã£ããŒãããã®ãäžè¬çã«ãªã£ãŠããããããŠãå
åºãªã·ã¹ãã ãœãããŠã§ã¢ãèšè¿°ããäžã§ãæé»çãªè€éæ§ãžã®å¯ŸåŠã¯äžå¿çãªããŒãã ã
*ã¹ã¬ãã (thread)* ã¯ã䞊è¡æ§ãè¡šçŸããæ段ã®äžã€ã ãã¹ã¬ããã䜿ãããšã§ããªãã¬ãŒãã£ã³ã°ã·ã¹ãã ã«ãã£ãŠã¹ã±ãžã¥ãŒã«ããããããŒããå
±æããç¬ç«ããå®è¡ã³ã³ãã¯ã¹ããå©çšã§ãããããããJava ã«ãããŠã¹ã¬ããçæã¯ã³ã¹ããé«ãã®ã§ãå
žåçã«ã¯ã¹ã¬ããããŒã«ã䜿ãããšã§ãã¹ã¬ããããªãœãŒã¹ãšããŠç®¡çããå¿
èŠããããããã¯ãããã°ã©ãã«ãšã£ãŠãããªãè€éããšé«ãçµå床ãçã¿åºããã€ãŸããã¢ããªã±ãŒã·ã§ã³ããžãã¯ãšãããã䜿çšããæœåšçãªãªãœãŒã¹ãåé¢ããã®ãé£ãããªãã
ãã®è€éãã¯ãåºå (fan-out) ã®å€§ãããµãŒãã¹ãäœæããéã«ããšãããæããã«ãªããããããã®åä¿¡ãªã¯ãšã¹ãããã¯ãã·ã¹ãã ã®ããã«å¥ã®éå±€ã«å¯Ÿããå€æ°ã®ãªã¯ãšã¹ããçããããããã®ã·ã¹ãã ã«ãããŠãã¹ã¬ããããŒã«ã¯åéå±€ã§ã®ãªã¯ãšã¹ãã®å²åã«ãã£ãŠãã©ã³ã¹ãä¿ã€ããã«ç®¡çãããå¿
èŠããããããã¹ã¬ããããŒã«ã§ç®¡çã«å€±æãããšããã®åœ±é¿ã¯ä»ã®ã¹ã¬ããããŒã«ã«ãåºãã£ãŠããŸãã
ãŸããå
åºãªã·ã¹ãã ã¯ã¿ã€ã ã¢ãŠããšãã£ã³ã»ã«ã«ã€ããŠãæ€èšããå¿
èŠããããã©ã¡ãã«å¯ŸåŠããã«ãããããªãâå¶åŸ¡ã¹ã¬ããâãå°å
¥ããå¿
èŠãããã®ã§ãåé¡ãããã«è€éã«ãªããã¡ãªã¿ã«ãããã¹ã¬ããã®ã³ã¹ããå®ããªãåé¡ã¯äœæžã§ãããã¹ã¬ããããŒã«ãå¿
èŠãªããªããã¿ã€ã ã¢ãŠãããã¹ã¬ãããæŸæ£ããããšãã§ããè¿œå ã®ãªãœãŒã¹ç®¡çãå¿
èŠãªãããã ã
ãã®ããã«ããªãœãŒã¹ç®¡çã¯ã¢ãžã¥ãŒã«æ§ãå±ããããã®ã ã
### Future
Future ã§äžŠè¡æ§ã管çããããFuture ã¯ã䞊è¡æäœãšãªãœãŒã¹ç®¡çãççµåã«ãããäŸãã°ã[Finagle][Finagle] ã¯ããããªæ°ã®ã¹ã¬ããäžã§äžŠè¡æäœãå¹ççã«å€éåãããScala ã«ã¯è»œéãªã¯ããŒãžã£ãªãã©ã«æ§æãããã®ã§ãFuture ã®ããã«æ°ããªæ§æãèŠããå¿
èŠããªããã»ãšãã©ã®ããã°ã©ãã«ãšã£ãŠèªç¶ã«æ±ããã
Future ã¯ãããã°ã©ãã䞊è¡èšç®ã宣èšçãªã¹ã¿ã€ã«ã§è¡šçŸã§ããããã«ãããFuture ã¯åæå¯èœã§ããŸãèšç®ã®å€±æãäžå®ã®ååã«åºã¥ããŠåŠçã§ãããããããæ§è³ªã¯ Future ã¯é¢æ°åããã°ã©ãã³ã°èšèªã«ãšãŠãé©ããŠãããæšå¥šãããã¹ã¿ã€ã«ã ãšç¢ºä¿¡ããŠããã
*çæãã Future ãå€æãããã*Future ã®å€æã¯ã倱æã®äŒæããã£ã³ã»ã«ã®éç¥ãè¡ãããããšãä¿èšŒãããŸãããã°ã©ãã Java ã¡ã¢ãªã¢ãã«ã®åœ±é¿ãæ€èšããå¿
èŠããªããªãã泚ææ·±ãããã°ã©ãã§ãããRPC ãé次çã« 10 åçºè¡ããŠçµæã衚瀺ããããã°ã©ãã以äžã®ããã«æžããŠããŸããããããªã:
val p = new Promise[List[Result]]
var results: List[Result] = Nil
def collect() {
doRpc() onSuccess { result =>
results = result :: results
if (results.length < 10)
collect()
else
p.setValue(results)
} onFailure { t =>
p.setException(t)
}
}
collect()
p onSuccess { results =>
printf("Got results %s\n", results.mkString(", "))
}
ããã°ã©ãã¯ãRPC ã®å€±æã確å®ã«äŒæããããã«ãã³ãŒãã«å¶åŸ¡ãããŒãããã€ãæ¿å
¥ããå¿
èŠããããããã«æªãããšã«ããã®ã³ãŒãã¯ééã£ãŠããïŒ `results` ã `volatile` ãšããŠå®£èšããŠããªãã®ã§ãç¹°ãè¿ãããšã« `results` ãäžã€åã®å€ãä¿æããŠããããšãä¿èšŒã§ããªããJavaã®ã¡ã¢ãªã¢ãã«ã¯ãæ²¹æãªããªãç£ã ãããã幞ããªããšã«ã宣èšçã¹ã¿ã€ã«ã䜿ãã°ãããã®èœãšãç©Žãå
šãŠé¿ããããšãã§ãã:
def collect(results: List[Result] = Nil): Future[List[Result]] =
doRpc() flatMap { result =>
if (results.length < 9)
collect(result :: results)
else
Future.value(result :: results)
}
collect() onSuccess { results =>
printf("Got results %s\n", results.mkString(", "))
}
ããã§ã¯ `flatMap` ã䜿ã£ãŠæäœãé åºä»ãããåŠçãé²ãã«ã€ããŠãªã¹ãã®å
é ã«çµæãè¿œå ããŠãããããã¯ãé¢æ°åããã°ã©ãã³ã°ã®äžè¬çãªã€ãã£ãªã ã Future ã«çœ®ãæãããã®ã ãããã¯æ£ããåäœããã ãã§ãªããå¿
èŠãªâããŸããªãâãå°ãªããªããããšã©ãŒã®æž©åºãæžãããããŠèªã¿ãããã
*Future ã®ã³ã³ãããŒã¿ (combinator) ã䜿ããã*`Future.select`, `Future.join`, `Future.collect` ã¯ãè€æ°ã® Future ãçµã¿åãããŠæäœããéã®äžè¬çãªãã¿ãŒã³ãäœç³»åããŠããã
### ã³ã¬ã¯ã·ã§ã³
䞊è¡ã³ã¬ã¯ã·ã§ã³ã®è©±é¡ã¯ãç§èŠãšãæ©åŸ®ãšããã°ããšãFUD ã«æºã¡ãŠããããããã¯ã倧æµã®å®è·µçãªç¶æ³ã«ãããŠã¯åãã«è¶³ããªãåé¡ã : ç®çãæããããã«ã¯ããã€ã§ãæãåçŽã§ãæãéå±ã§ãæãæšæºçãªã³ã¬ã¯ã·ã§ã³ããå§ããããåæåã³ã¬ã¯ã·ã§ã³ã§ã¯äžæããããªãããšã*åãã*åã«ã䞊è¡ã³ã¬ã¯ã·ã§ã³ãæã«åã£ãŠã¯ãããªããJVM ã¯ãåæãäœã³ã¹ãã§å®çŸããæŽç·Žãããæ©æ§ãæã£ãŠããããã®å¹çã«åã¯é©ãã ããã
äžå€ (immutable) ã³ã¬ã¯ã·ã§ã³ã§ç®çãæããããªããããã䜿ãããäžå€ã³ã¬ã¯ã·ã§ã³ã¯åç
§ééãªã®ã§ã䞊è¡ã³ã³ããã¹ãã§ã®æšè«ãç°¡åã«ãªããäžå€ã³ã¬ã¯ã·ã§ã³ã®å€æŽã¯ãäž»ã«ïŒ`var` ã»ã«ã `AtomicReference` ãæãïŒçŸåšã®å€ãžã®åç
§ãæŽæ°ããããšã§è¡ããäžå€ã³ã¬ã¯ã·ã§ã³ã®å€æŽã¯æ³šæãå¿
èŠã ãä»ã®ã¹ã¬ãããžäžå€ã³ã¬ã¯ã·ã§ã³ãå
¬éããã«ã¯ã`AtomicReference` ã¯åè©Šè¡ãå¿
èŠã ãã`var` å€æ°ã¯ `volatile` ãšããŠå®£èšããªããã°ãªããªãã
å¯å€ (mutable) ãªäžŠè¡ã³ã¬ã¯ã·ã§ã³ã¯è€éãªåäœãããã ãã§ãªããJava ã¡ã¢ãªã¢ãã«ã®åŸ®åŠãªéšåãå©çšããã®ã§ãç¹ã«æŽæ°ãå
¬éããæ¹æ³ãªã©ã®æé»çãªæåãç解ããŠããããåæåã³ã¬ã¯ã·ã§ã³ã®æ¹ãåæã¯ç°¡åã ã䞊è¡ã³ã¬ã¯ã·ã§ã³ã§ã¯ `getOrElseUpdate` ã®ãããªæäœãæ£ããå®è£
ã§ããªãããç¹ã«äžŠè¡ã³ã¬ã¯ã·ã§ã³ã®åæã¯ãšã©ãŒã®æž©åºã«ãªãã
## å¶åŸ¡æ§é
é¢æ°åã¹ã¿ã€ã«ã®ããã°ã©ã ã¯ãåŸæ¥ã®å¶åŸ¡æ§é ãå°ãªããªãããŸãã宣èšåã¹ã¿ã€ã«ã§æžãããŠãããšèªã¿ãããããšãå€ããããããã¹ã¿ã€ã«ã§ã¯ãå
žåçã«ã¯ããžãã¯ãããã€ãã®å°ããªã¡ãœãããé¢æ°ã«å解ããããããäºãã« `match` åŒã§è²ŒãåãããããŸããé¢æ°åããã°ã©ã ã¯ãããåŒæåãšãªãåŸåããã: ã€ãŸããæ¡ä»¶åŒã®ããããã®åå²ã¯åãåã®å€ãèšç®ãã`for (..) yield` ã¯å
å
(comprehension) ãèšç®ããããŸããååž°ã®å©çšãäžè¬çã ã
### ååž°
*ååž°è¡šçŸã䜿ããšãåé¡ããã°ãã°ç°¡æœã«èšè¿°ã§ããã*ãããŠã³ã³ãã€ã©ã¯ãæ«å°ŸåŒã³åºãã®æé©åãé©çšã§ããã³ãŒããæ£èŠã®ã«ãŒãã«çœ®ãæããïŒæ«å°Ÿæé©åãé©çšãããã㯠`@tailrec` ã¢ãããŒã·ã§ã³ã§ç¢ºèªã§ããïŒã
ããŒãã® fix-down ã¢ã«ãŽãªãºã ã®ã極ããŠæšæºçãªåœä»€åããŒãžã§ã³ãæ€èšããã:
def fixDown(heap: Array[T], m: Int, n: Int): Unit = {
var k: Int = m
while (n >= 2*k) {
var j = 2*k
if (j < n && heap(j) < heap(j + 1))
j += 1
if (heap(k) >= heap(j))
return
else {
swap(heap, k, j)
k = j
}
}
}
ãã®ã³ãŒãã§ã¯ãwhile ã«ãŒãã«å
¥ããã³ã«äžã€åã®å埩ã§å€æŽãããç¶æ
ãåç
§ãããåå€æ°ã®å€ã¯ãã©ã®åå²ãåããã«äŸåããããããŠãæ£ããäœçœ®ãèŠã€ãããšã«ãŒãã®äžç€ã§ `return` ããïŒéãèªè
ã¯ããã€ã¯ã¹ãã©ã® ["Go To Statement Considered Harmful"](http://www.u.arizona.edu/~rubinson/copyright_violations/Go_To_Considered_Harmful.html) ^[蚳泚: [ãšãã¬ãŒã»ãã€ã¯ã¹ãã©](http://ja.wikipedia.org/wiki/%E3%82%A8%E3%83%89%E3%82%AC%E3%83%BC%E3%83%BB%E3%83%80%E3%82%A4%E3%82%AF%E3%82%B9%E3%83%88%E3%83%A9)ã¯ãæ§é åããã°ã©ãã³ã°ã®æå±è
ã圌ãå·çãããšãã»ã€ "Go To Statement Considered Harmful" ã¯âGOTOæ害è«âã®ç«¯ç·ãšããŠæåã] ã«åæ§ã®è°è«ãããããšã«æ°ã¥ããšæãïŒã
ïŒæ«å°ŸïŒååž°ã«ããå®è£
ãæ€èšããã^[[Finagle's heap
balancer](https://github.com/twitter/finagle/blob/master/finagle-core/src/main/scala/com/twitter/finagle/loadbalancer/Heap.scala#L41)ãã]:
@tailrec
final def fixDown(heap: Array[T], i: Int, j: Int) {
if (j < i*2) return
val m = if (j == i*2 || heap(2*i) < heap(2*i+1)) 2*i else 2*i + 1
if (heap(m) < heap(i)) {
swap(heap, i, m)
fixDown(heap, m, j)
}
}
.LP ããã§ã¯ããã¹ãŠã®å埩ã¯æ確ã«å®çŸ©ãããçœçŽã®ç¶æ
ããéå§ããããŸããåç
§ã»ã«ãååšããªãã®ã§äžå€åŒ (invariant) ãæ°å€ãèŠåºããããã®ã¡ãœããã¯ããæšè«ãããããããèªã¿ããããããã«ãæ§èœé¢ã®ããã«ãã£ããªããã¡ãœããã¯æ«å°Ÿååž°ãªã®ã§ãã³ã³ãã€ã©ããããæšæºçãªåœä»€åã®ã«ãŒããžãšå€æããããã ã
### Return
åç¯ã§ã¯ååž°ã䜿ãã¡ãªããã玹ä»ãããããšã¯ããåœä»€åã®æ§é ãç¡äŸ¡å€ã ãšããããã§ã¯ãªããå€ãã®å Žåãèšç®ãæ©æã«çµäºããæ¹ããçµç¹ã®å¯èœæ§ãããå
šãŠã®äœçœ®ã«æ¡ä»¶åå²ãæã€ãããé©åã ãå®éã«ãäžèšã® `fixDown` ãããŒãã®çµç«¯ã«éãããš `return` ã«ãã£ãŠæ©æã«çµäºããã
`return` ã䜿ããšãåå²ãæžãããŠäžå€åŒ (invariant) ãå®ããããšãã§ãããããã«ãããå
¥ãåãæžã£ãŠã³ãŒããè¿œãããããªãã ãã§ãªããåŸç¶ã®ã³ãŒãã®æ£åœæ§ãæšè«ãããããªãïŒé
åã®ç¯å²å€ãã¢ã¯ã»ã¹ããªãããšã確èªããå ŽåãšãïŒãããã¯ã"ã¬ãŒã"ç¯ã§ç¹ã«æçšã :
def compare(a: AnyRef, b: AnyRef): Int = {
if (a eq b)
return 0
val d = System.identityHashCode(a) compare System.identityHashCode(b)
if (d != 0)
return d
// slow path..
}
`return` ã䜿ã£ãŠãã³ãŒããæå¿«ã«ããŠèªã¿ããããé«ãããããã ããåœä»€åèšèªã§ã®ãããªäœ¿ãæ¹ãããŠã¯ãããªããã€ãŸããèšç®çµæãè¿ãããã« `return` ã䜿ãã®ã¯é¿ãããã
def suffix(i: Int) = {
if (i == 1) return "st"
else if (i == 2) return "nd"
else if (i == 3) return "rd"
else return "th"
}
.LP ãšæžã代ããã«äžèšã®ããã«æžãã:
def suffix(i: Int) =
if (i == 1) "st"
else if (i == 2) "nd"
else if (i == 3) "rd"
else "th"
.LP ããããmatch
åŒã䜿ãã®ãããåªããæ¹æ³ã :
def suffix(i: Int) = i match {
case 1 => "st"
case 2 => "nd"
case 3 => "rd"
case _ => "th"
}
ãªããã¯ããŒãžã£ã®å
éšã§ `return` ã䜿ããšç®ã«èŠããªãã³ã¹ããçºçããå Žåãããã®ã§æ³šæãããã
seq foreach { elem =>
if (elem.isLast)
return
// process...
}
.LP ãã® `return` ã¯ããã€ãã³ãŒãã§ã¯äŸå€ã `throw` ãš `catch` ããã³ãŒããšããŠå®è£
ãããã®ã§ãå®è¡é »åºŠã®é«ãã³ãŒãå
ã§äœ¿ããšæ§èœã«åœ±é¿ãäžããã
### `for`ã«ãŒããšå
å
`for` ã䜿ããšãã«ãŒããšéçŽãç°¡æœãã€èªç¶ã«è¡šçŸã§ããã`for` ã¯ãå€æ°ã®ã·ãŒã±ã³ã¹ãå¹³åŠå (flatten) ããå Žåã«ç¹ã«æçšã ã`for` ã®æ§æã¯ãå
éšçã«ã¯ã¯ããŒãžã£ãå²ãåœãŠãŠãã£ã¹ãããããŠããããšãèŠãé ããŠãããããã«ããäºæããªãã³ã¹ããçºçããããäºæ³å€ã®æåã瀺ããããããäŸãã°ã
for (item <- container) {
if (item != 2) return
}
.LP ãã®ã³ãŒã㯠`container` ãé
延è©äŸ¡ããããšã©ã³ã¿ã€ã ãšã©ãŒãçºçããããã«ãã return
ãéå±æç (nonlocal) ã«è©äŸ¡ãããŠããŸãïŒ
ãããã®çç±ãããã³ãŒããæçã«ããããã§ããå Žåãé€ããŠã`for` ã®ä»£ããã« `foreach`, `flatMap`, `map`, `filter` ãçŽæ¥åŒã³åºãã®ãè¯ãããšãå€ãã
.LP ïŒèš³è
ã«ããè£è¶³: Scala ã® for åŒã¯ `foreach`, `flatMap`, `map`, `withFilter` ãåŒã³åºãç³è¡£æ§æã§ãã«ãŒãå
ã®åŒã¯ãã³ã³ãã€ã«æã«ãããã®ã¡ãœããã«æž¡ãããå¿åé¢æ°ã«å€æããããäŸãã°ãäžèšã® for åŒã¯:
container foreach { item =>
if (item != 2) return
}
.LP ãšããã³ãŒããšããŠå®è¡ããããæ¬ã¬ã€ãã§ã¯ãæåãããã®ããã«èšè¿°ããããšãæšå¥šããŠããã
ãšããã§ããã¹ãããå¿åé¢æ°ã§ã® `return` åŒã¯ãã©ã³ã¿ã€ã ãšã©ãŒã§ãã `NonLocalReturnException` ã® `throw` ãš `catch` ã«å€æãããããã®å Žåãfor åŒã®äžã® `container` ãé
延è©äŸ¡ããããš `return` åŒã®æåãæå³ããªããã®ã«ãªãå Žåãããã詳现ã«èå³ãããèªè
ã¯[ãã¡ãã®è°è«](https://github.com/scalajp/effectivescala/commit/8b448ef819e6d87d21fa78310b84fc72593b0226#commitcomment-996948)ãåç
§ããŠã»ãããïŒ
### `require` ãš `assert`
`require` ãš `assert` ã¯ãã©ã¡ããå®è¡å¯èœãªããã¥ã¡ã³ããšããŠæ©èœããããããã¯ãåã·ã¹ãã ãèŠæ±ãããäžå€æ¡ä»¶ (invariant) ãè¡šçŸã§ããªãç¶æ³ã§æçšã ã`assert` ã¯ãã³ãŒããä»®å®ããïŒå
éšãããã¯å€éšã®ïŒ*äžå€æ¡ä»¶*ãè¡šçŸããããã«äœ¿ããããäŸãã°ã
val stream = getClass.getResourceAsStream("someclassdata")
assert(stream != null)
äžæ¹ã§ã`require` 㯠API ã®å¥çŽãè¡šçŸããããã«äœ¿ããã:
def fib(n: Int) = {
require(n > 0)
...
}
## é¢æ°åããã°ã©ãã³ã°
*å€æå*ããã°ã©ãã³ã°ã¯ãç¹ã«é¢æ°åããã°ã©ãã³ã°ãšäžç·ã«çšããããšã§æ°ã
ã®å©çãããããããã®ã¹ã¿ã€ã«ã¯ã¹ããŒããã«ãªå€æŽãããå€ã®å€æãéèŠããåç
§éé (referentially transparent) ãªã³ãŒããçã¿åºãããã匷åãªäžå€åŒ (invariant) ãæäŸããã®ã§ãæšè«ã容æã«ãªããã±ãŒã¹ã¯ã©ã¹ããã¿ãŒã³ããããæ§é å代å
¥ (destructuring binding) ãåæšè«ãã¯ããŒãžã£ãã¡ãœããã®è»œéãªçææ§æããã®ä»äºã®éå
·ã«ãªãã
### 代æ°çããŒã¿åãšããŠã®ã±ãŒã¹ã¯ã©ã¹
ã±ãŒã¹ã¯ã©ã¹ (case class) ã¯ã代æ°çããŒã¿å (algebraic data type) ããšã³ã³ãŒããã: ã±ãŒã¹ã¯ã©ã¹ã¯æ°å€ãã®ããŒã¿æ§é ãã¢ããªã³ã°ããã®ã«åœ¹ã«ç«ã¡ã匷åãªäžå€åŒãç°¡æœãªã³ãŒããšããŠæäŸãããã±ãŒã¹ã¯ã©ã¹ã¯ããã¿ãŒã³ããããšå
±ã«å©çšãããšç¹ã«æçšã ããã¿ãŒã³ãããã®è§£æåšã¯ãããã«åŒ·åãªéçä¿èšŒãæäŸããå
æ¬ç解æ (exhaustivity analysis) ãå®è£
ããŠããã
ã±ãŒã¹ã¯ã©ã¹ã§ä»£æ°çããŒã¿åããšã³ã³ãŒãããéã¯ã以äžã®ãã¿ãŒã³ã䜿ãã:
sealed trait Tree[T]
case class Node[T](left: Tree[T], right: Tree[T]) extends Tree[T]
case class Leaf[T](value: T) extends Tree[T]
.LP Tree[T]
åã«ã¯ Node
ãš Leaf
ã® 2 ã€ã®ã³ã³ã¹ãã©ã¯ã¿ããããåã sealed
ãšããŠå®£èšãããšãœãŒã¹ãã¡ã€ã«ã®å€ã§ã³ã³ã¹ãã©ã¯ã¿ãè¿œå ã§ããªããªãã®ã§ãã³ã³ãã€ã©ã«å
æ¬ç解æãè¡ãããããšãã§ããã
ãã¿ãŒã³ããããšå
±ã«äœ¿ããšãäžèšã®ã¢ããªã³ã°ã¯ç°¡æœã§ãããã€âæããã«æ£ããâã³ãŒãã«ãªã:
def findMin[T <: Ordered[T]](tree: Tree[T]) = tree match {
case Node(left, right) => Seq(findMin(left), findMin(right)).min
case Leaf(value) => value
}
æšæ§é ã®ãããªååž°çæ§é ã¯ã代æ°çããŒã¿åã®å€å
žçãªå¿çšãå ãããã代æ°çããŒã¿åãæçšãªé åã¯ãããããã£ãšå€§ãããç¹ã«ãç¶æ
æ©æ¢°ã«ããçŸããçŽå (disjoint union) ã¯ã代æ°çããŒã¿åã§å®¹æã«ã¢ãã«åã§ããã
### Option
`Option` åã¯ã空ã®ç¶æ
(`None`) ãšæºããããç¶æ
(`Some(value)`) ã®ããããã§ããã³ã³ããã ãOption 㯠`null` ã®å®å
šãªä»£æ¿æ段ãæäŸããã®ã§ããã€ã§ãå¯èœãªéãå©çšããã¹ãã ã`Option` ã¯ïŒããã ãèŠçŽ ãäžã€ã®ïŒã³ã¬ã¯ã·ã§ã³ãªã®ã§ãã³ã¬ã¯ã·ã§ã³æäœãå©çšã§ããã䜿ããããªã!
以äžã®ããã«æžãã®ã¯å®å
šã§ã¯ãªã:
var username: String = null
...
username = "foobar"
.LP 以äžã®ããã«æžãã:
var username: Option[String] = None
...
username = Some("foobar")
.LP Option
åã¯ãusername
ã空ã§ãããã®ãã§ãã¯ãéçã«åŒ·å¶ããŠãããã®ã§ããå®å
šã ã
`Option` å€ã®æ¡ä»¶å®è¡ã¯ `foreach` ã䜿ãã¹ãã ã以äžã®ããã«æžã代ããã«:
if (opt.isDefined)
operate(opt.get)
.LP 以äžã®ããã«æžãã:
opt foreach { value =>
operate(value)
}
ãã®ã¹ã¿ã€ã«ã¯å¥åŠã«æãããããããªãããããåªããå®å
šæ§ãšç°¡æœããæäŸããïŒäŸå€ãåŒãèµ·ãããã `get` ã¯åŒãã§ããªãïŒã空ã®å Žåãšå€ãæã€å Žåã®äž¡æ¹ã®åå²ããããããªãããã¿ãŒã³ãããã䜿ãã:
opt match {
case Some(value) => operate(value)
case None => defaultAction()
}
.LP ããããããå€ããªãå Žåã«ããã©ã«ãå€ã§è¯ããªã getOrElse
ãããã
operate(opt getOrElse defaultValue)
`Option` ã®æ¿«çšã¯ãããªã: ããã[*Null Object*](http://en.wikipedia.org/wiki/Null_Object_pattern) ã®ãããªç®çã«ãã£ãããã©ã«ãå€ããããªãã代ããã«ããã䜿ããã
ãŸã `Option` ã¯ãnull ã«ãªãåŸãå€ãã©ããã§ãããæ±ããããã³ã³ã¹ãã©ã¯ã¿ãšå
±ã«äœ¿ãã:
Option(getClass.getResourceAsStream("foo"))
.LP 㯠Option[InputStream]
ã ããgetResourceAsStream
ã null
ãè¿ãå Žåã« None
ãè¿ãã
### ãã¿ãŒã³ããã
ãã¿ãŒã³ããã (`x match { ...`) ã¯ãããŸãæžããã Scala ã³ãŒãã®ããããå Žæã§äœ¿ãããããã¿ãŒã³ãããã¯ãæ¡ä»¶å®è¡ãšå解ããããŠäžã€ã®æ§æç©ãžã®ãã£ã¹ãã® 3 ã€ãèåãããããŸã䜿ãããšã§æå¿«ããšå®å
šæ§ã®åæ¹ãé«ããããã
åããšã«åŠçãåãæ¿ããå®è£
ã«ãã¿ãŒã³ãããã䜿ã:
obj match {
case str: String => ...
case addr: SocketAddress => ...
ãŸãããã¿ãŒã³ãããã¯å解ãšçµã¿åãããããšã§æãè¯ãåãã瀺ããã±ãŒã¹ã¯ã©ã¹ã«ãããããéã¯ã以äžã®ããã«æžããã«:
animal match {
case dog: Dog => "dog (%s)".format(dog.breed)
case _ => animal.species
}
.LP 以äžã®ããã«æžã:
animal match {
case Dog(breed) => "dog (%s)".format(breed)
case other => other.species
}
[ã«ã¹ã¿ã æœåºå](http://www.scala-lang.org/node/112)ãæžãã®ã¯ã³ã³ã¹ãã©ã¯ã¿ (`apply`) ãéè€ããå Žåã®ã¿ãšããããããªããã°äžèªç¶ã«ãªãå¯èœæ§ãããã
ããã©ã«ãå€ã«ãã£ãšæå³ãããå Žåã¯ãæ¡ä»¶å®è¡ã«ãã¿ãŒã³ãããã䜿ãã¹ãã§ã¯ãªããã³ã¬ã¯ã·ã§ã³ã©ã€ãã©ãªã¯ãæ®é㯠`Option` ãè¿ãã¡ãœãããæäŸããŠããã以äžã®ããã«æžãã®ã¯é¿ããã¹ãã :
val x = list match {
case head :: _ => head
case Nil => default
}
.LP ãªããªãã
val x = list.headOption getOrElse default
.LP ã®æ¹ãããçããŠç®çãäŒãããããããã ã
### éšåé¢æ°
Scala ã¯ãéšåé¢æ° (`PartialFunction`) ãå®çŸ©ã§ããæ§æçãªç°¡æèšæ³ãæäŸãã:
val pf: PartialFunction[Int, String] = {
case i if i%2 == 0 => "even"
}
.LP ãŸããããã㯠orElse
ãšçµã¿åãããããã
val tf: (Int => String) = pf orElse { case _ => "odd"}
tf(1) == "odd"
tf(2) == "even"
éšåé¢æ°ã¯å€ãã®å Žé¢ã«çŸãããïŒ`PartialFunction` ã§å¹ççã«ãšã³ã³ãŒãã§ãããã¡ãœããã®åŒæ°ãšããŠå©çšããäŸ:
trait Publisher[T] {
def subscribe(f: PartialFunction[T, Unit])
}
val publisher: Publisher[Int] = ...
publisher.subscribe {
case i if isPrime(i) => println("found prime", i)
case i if i%2 == 0 => count += 2
/* ignore the rest */
}
.LP ãŸããOption
ãè¿ã代ããã«:
// Attempt to classify the the throwable for logging.
type Classifier = Throwable => Option[java.util.logging.Level]
.LP PartialFunction
ã§è¡šçŸããæ¹ãè¯ãå Žé¢ããã:
type Classifier = PartialFunction[Throwable, java.util.Logging.Level]
.LP ãªããªããPartialFunction
ã®çµã¿åããå¯èœãªæ§è³ªã倧ãã«æŽ»ãããããã :
val classifier1: Classifier
val classifier2: Classifier
val classifier = classifier1 orElse classifier2 orElse { _ => java.util.Logging.Level.FINEST }
### æ§é å代å
¥
æ§é å代å
¥ (destructuring binding^[蚳泚: destructuring binding ã«ã¯âæ§é å代å
¥âãâåé
æçžâãªã©ã®èš³ãããã詳现ã«ã€ããŠã¯[ãã¡ãã®è°è«](https://github.com/scalajp/effectivescala/issues/4)ãåç
§ããŠã»ããã]) ã¯å€ã®ä»£å
¥ã®äžçš®ã§ããããã¿ãŒã³ããããšé¢é£ããŠãããæ§é å代å
¥ãšãã¿ãŒã³ãããã¯åãã¡ã«ããºã ãå©çšããããäŸå€ã®å¯èœæ§ã蚱容ããªãããã«ãå³å¯ã«éžæè¢ãäžã€ã ãã®ãšãã«ã®ã¿é©çšã§ãããæ§é å代å
¥ã¯ãç¹ã«ã¿ãã«ãã±ãŒã¹ã¯ã©ã¹ã䜿ãéã«æçšã ã
val tuple = ('a', 1)
val (char, digit) = tuple
val tweet = Tweet("just tweeting", Time.now)
val Tweet(text, timestamp) = tweet
### é
延è©äŸ¡
Scala ã«ãããŠã`lazy` ã§ä¿®é£Ÿããã `val` ãã£ãŒã«ãïŒé
延ãã£ãŒã«ãïŒã¯*å¿
èŠã«ãªã£ããšãã«*èšç®ãããããªããªããScala ã§ã¯ãã£ãŒã«ããšã¡ãœããã¯ç䟡ã ããã ïŒScala ã®ãã£ãŒã«ãã Java ã®ãã£ãŒã«ããšåããã®ã«ãããå Žå㯠`private[this]` ã䜿ãïŒã
lazy val field = computation()
.LP ã¯ïŒããããïŒä»¥äžã®ãããªã³ãŒãã®ç°¡æèšæ³ã :
var _theField = None
def field = if (_theField.isDefined) _theField.get else {
_theField = Some(computation())
_theField.get
}
.LP ããªãã¡ãèšç®ããŠçµæãèšæ¶ãããé
延ãã£ãŒã«ãã¯ããã®ç®çã®ããã«äœ¿ãããã«ãããããããæå³è«ã«ãã£ãŠé
延ãèŠæ±ãããå Žåã«é
延è©äŸ¡ã䜿ãã¹ãã§ã¯ãªãããã®ãããªå Žåã«ã¯æ瀺çã«æžããæ¹ãããããªããªããããã«ããã³ã¹ãã¢ãã«ãæ確ã«ãªããå¯äœçšããã粟å¯ã«å¶åŸ¡ã§ããããã ã
é
延ãã£ãŒã«ãã¯ã¹ã¬ããã»ãŒãã ã
### åååŒã³åºã
ã¡ãœããã®åŒæ°ã¯ãååã«ãã£ãŠç¹å®ããŠãããããã®æå³ã¯ãåŒæ°ãå€ã§ã¯ãªããç¹°ãè¿ãè©äŸ¡ãããã*èšç®*ã«å¯ŸããŠæçžãããšããããšã ãå€æž¡ãã®æèãæåŸ
ããŠããåŒã³åºãåŽãé©ãããªãããã«ããã®æ©èœã¯æ³šææ·±ãé©çšãã¹ãã ããã®æ©èœã®ç®çã¯ãæ§æçã«èªç¶ãª DSL ãæ§ç¯ããããšã«ãããç¹ã«ãæ°ããå¶åŸ¡æ§é ããããããæåããèšèªã«åãã£ãŠããæ©èœã§ãããã®ããã«èŠããããšãã§ããã
åååŒã³åºã (call-by-name) ã¯ããã®ãããªå¶åŸ¡æ§é ã«æž¡ãããã®ããåŒã³åºãåŽã«ãšã£ãŠæãããããªãèšç®ã®çµæã§ã¯ãªãâãããã¯âã§ããããšãæãããªå Žåã«ã®ã¿äœ¿ãããåæ§ã«ãåååŒã³åºãã®åŒæ°ã¯ãæåŸã®åŒæ°ãªã¹ãã®æåŸã®äœçœ®ã§ã®ã¿äœ¿ãã¹ãã ãåŒæ°ãåååŒã³åºãã§ããã¡ãœããã®ååã¯ãåŒã³åºãåŽã«ãã®ããšãåããããã«åœåãããã
å€ãè€æ°åèšç®ãããããŠãç¹ã«ãã®èšç®ãå¯äœçšãæã€ãªãæ瀺çãªé¢æ°ã䜿ãã:
class SSLConnector(mkEngine: () => SSLEngine)
.LP æäŸè
ã®æå³ã¯ã·ã°ããã£ããæããã§ãããåŒã³åºãåŽãé©ãããããšããªãã
### `flatMap`
`flatMap` 㯠`map` ãš `flatten` ãçµã¿åããããã®ã§ããã®å·§åŠãªåãšçŽ æŽãããå®çšæ§ã«ãã£ãŠç¹å¥ãªæ³šç®ã济ã³ãã«å€ããã`flatMap` ã¯ããã®ä»²éã® `map` ãšåæ§ã«ã`Future` ã `Option` ã®ãããªåŸæ¥ãšã¯ç°ãªãã³ã¬ã¯ã·ã§ã³ã«ãããŠããã°ãã°å©çšå¯èœã ããã®æ¯ãèãã¯ã·ã°ããã£ããæããã ããã `Container[A]` ã«ã€ããŠã
flatMap[B](f: A => Container[B]): Container[B]
.LP flatMap
ã¯ã³ã¬ã¯ã·ã§ã³ã®èŠçŽ ã«å¯ŸããåèŠçŽ ããæ°ããã³ã¬ã¯ã·ã§ã³ãäœãåºãé¢æ° f
ãåŒã³åºããåŸããã®çæããïŒå
šãŠã®ïŒã³ã¬ã¯ã·ã§ã³ãå¹³åŠå (flatten) ããçµæãè¿ããäŸãã°ãåãæåãç¹°ãè¿ããªããã㪠2 æåã®æååã®é åãå
šãŠååŸããã«ã¯:
val chars = 'a' to 'z'
val perms = chars flatMap { a =>
chars flatMap { b =>
if (a != b) Seq("%c%c".format(a, b))
else Seq()
}
}
.LP ããã¯ãäžèšã®ã³ãŒããããç°¡æœã«èšè¿°ã§ããç³è¡£æ§æã§ãã for å
å
èšæ³ãšïŒããããïŒç䟡ã :
val perms = for {
a <- chars
b <- chars
if a != b
} yield "%c%c".format(a, b)
`flatMap` 㯠`Option` ãæ±ãéã«ãã°ãã°æçšã ã`flatMap` ã䜿ããšãOption ã®é£éãç³ã¿èŸŒãã§äžã€ã«ã§ããã
val host: Option[String] = ...
val port: Option[Int] = ...
val addr: Option[InetSocketAddress] =
host flatMap { h =>
port map { p =>
new InetSocketAddress(h, p)
}
}
.LP ããã for
ã䜿ãã°ãã£ãšç°¡æœã«èšè¿°ã§ããã
val addr: Option[InetSocketAddress] = for {
h <- host
p <- port
} yield new InetSocketAddress(h, p)
`Future` ã«ããã `flatMap` ã®äœ¿ãæ¹ã¯âFutureâã®ç« ã§è°è«ããã
## ãªããžã§ã¯ãæåããã°ã©ãã³ã°
Scala ã®å倧ãã®å€§éšåã¯ããªããžã§ã¯ãã·ã¹ãã ã«ãããã®ã ãScala ã¯*ãã¹ãŠã®å€*ããªããžã§ã¯ãã§ãããšããæå³ã§*çŽç²ãª*èšèªã§ãããããªããã£ãåãšè€ååã®éã«éãã¯ãªããScala ã«ã¯ããã¯ã¹ã€ã³ (mixin) ã®æ©èœããããããã¯ã¹ã€ã³ã䜿ããšããã£ãšçŽäº€çãã€æ®µéçã«ã¢ãžã¥ãŒã«ãçµã¿åãããããã ãã§ãªããããã«ã³ã³ãã€ã«æã®éçãªåæ€æ»ãæè»ã«çµã¿åãããŠããã®æ©æµããã¹ãŠäº«åã§ããã
ããã¯ã¹ã€ã³ã·ã¹ãã ã®èæ¯ã«ããåæ©ã¯ãåŸæ¥ã®äŸåæ§æ³šå
¥ (dependency injection) ãäžèŠã«ããããšã ããã®âã³ã³ããŒãã³ãã¹ã¿ã€ã«âã®ããã°ã©ãã³ã°ã®æ¥µèŽããã [Cake
ãã¿ãŒã³](http://jonasboner.com/real-world-scala-dependency-injection-di/)ïŒ[æ¥æ¬èªèš³](http://eed3si9n.com/ja/real-world-scala-dependency-injection-di)ïŒã ã
### äŸåæ§æ³šå
¥
ããããScala èªèº«ããâå€å
žçãªâïŒã³ã³ã¹ãã©ã¯ã¿ãžã®ïŒäŸåæ§æ³šå
¥ãå©çšããéã®é¢åãªæ§æããã»ãšãã©åãé€ããŠãããããšãåãã£ãã®ã§ãTwitter ã§ã¯ãããäŸåæ§æ³šå
¥ã䜿ãããã«ããŠãã: ããã¯ããæå¿«ã§ãäŸåæ§ã¯ãã¯ãïŒã³ã³ã¹ãã©ã¯ã¿ã®ïŒåã«ãã£ãŠãšã³ã³ãŒããããã¯ã©ã¹ãæ§ç¯ããæ§æã¯ãšãããŠãŠé£ãããªãæ±ãããããããã¯éå±ã§åçŽã ããããŸãããã*äŸåæ§æ³šå
¥ã¯ããã°ã©ã ãã¢ãžã¥ãŒã«åããããã«äœ¿ãã*ããããŠãç¹ã«*ç¶æ¿ããåæã䜿ãã*ãããã«ãããããã¢ãžã¥ãŒã«åãããŠãã¹ãã容æãªããã°ã©ã ã«ãªããç¶æ¿ãå¿
èŠãªç¶æ³ã«ééããããããèããŠã¿ãã: âããç¶æ¿ããµããŒãããªãèšèªã䜿ããšãããããã®ããã°ã©ã ãã©ã®ããã«æ§é åããã ããïŒâãšããã®çãã«ã¯èª¬åŸåããããããããªãã
éåžžãäŸåæ§æ³šå
¥ã«ã¯ãã¬ã€ãã䜿ãã
trait TweetStream {
def subscribe(f: Tweet => Unit)
}
class HosebirdStream extends TweetStream ...
class FileStream extends TweetStream ...
class TweetCounter(stream: TweetStream) {
stream.subscribe { tweet => count += 1 }
}
äžè¬çã«ã¯ã泚å
¥ããã®ã¯ä»ã®ãªããžã§ã¯ããçæãããªããžã§ã¯ãã§ãã*ãã¡ã¯ã㪠(factory)* ã ããã®å Žåãç¹åãããã¡ã¯ããªåã§ã¯ãªãã·ã³ãã«ãªé¢æ°ã䜿ãã¹ãã ãïŒèš³è
ã«ããè£è¶³: ã€ãŸããæ»ãå€ãæã€ããããé¢æ°ã¯ãã¡ã¯ããªãšã¿ãªãããšããããšïŒ
class FilteredTweetCounter(mkStream: Filter => TweetStream) {
mkStream(PublicTweets).subscribe { tweet => publicCount += 1 }
mkStream(DMs).subscribe { tweet => dmCount += 1 }
}
### ãã¬ã€ã
äŸåæ§æ³šå
¥ã䜿çšãããããšãã£ãŠãå
±éã®*ã€ã³ã¿ãã§ãŒã¹*ãããã¬ã€ã (trait) ã«å®è£
ãããå
±éã³ãŒããåæã«äœ¿ã£ãŠã¯ãªããªããšããããšã¯å
šããªããããã©ããããäžã€ã®å
·è±¡ã¯ã©ã¹ãè€æ°ã®ã€ã³ã¿ãã§ãŒã¹ïŒãã¬ã€ãïŒãå®è£
ãããããããããŸãå
±éã³ãŒããå
šãŠã®ã¯ã©ã¹ã§æšªæçã«åå©çšãããããããªãã®ã§ããã¬ã€ãã®äœ¿çšã¯åŒ·ãæšå¥šãããã
ãã¬ã€ãã¯çãããŠçŽäº€æ§ãä¿ãšã: åå²ã§ããæ©èœãäžã€ã®ãã¬ã€ãã®å¡ã«ããŠã¯ãããªããäºãã«çµã¿åãããé¢é£ããã¢ã€ãã¢ã®ãã¡æå°ã®ãã®ãèããããã«ãããäŸãã°ãIO ãè¡ãäœãããããšããã:
trait IOer {
def write(bytes: Array[Byte])
def read(n: Int): Array[Byte]
}
.LP ãããäºã€ã®æ¯ãèãã«åé¢ãã:
trait Reader {
def read(n: Int): Array[Byte]
}
trait Writer {
def write(bytes: Array[Byte])
}
.LP ãããŠãããããäºãã« new Reader with Writer
⊠ã®ããã«ããã¯ã¹ã€ã³ããŠãå
ã»ã©ã® IOer
ã圢æãããã€ã³ã¿ãŒãã§ã€ã¹ã®æå°åã¯ãããããçŽäº€æ§ãšã¢ãžã¥ãŒã«åãããããã
### å¯èŠæ§
Scala ã¯ãå¯èŠæ§ãå¶åŸ¡ããããã®éåžžã«è¡šçŸåã®é«ã修食åãæã€ããããã®ä¿®é£Ÿåã¯ãäœã*å
¬é API* ãšããŠæ§æããããå®çŸ©ããã®ã«äœ¿ãã®ã§éèŠã ãå
¬éãã API ã¯éå®ãããã¹ãã ãããã«ãã£ãŠãŠãŒã¶ãå®è£
ã®è©³çŽ°ã«ãã£ããäŸåããããšããªããªãããŸããäœè
ã API ãå€æŽããèœåãå¶éããããããã¯ãè¯ãã¢ãžã¥ãŒã«æ§ã«ãšã£ãŠæ¥µããŠéèŠã ãååçã«ãå
¬é API ãæ¡åŒµããã®ã¯çž®å°ãããããã¯ããã«ç°¡åã ããŸããã¢ãããŒã·ã§ã³ã貧匱ã ãšãã³ãŒãã®ãã€ããªã®åŸæ¹äºææ§ãå±ãããªãã
#### `private[this]`
`private` ã«æå®ããã¯ã©ã¹ã¡ã³ãã¯ã
private val x: Int = ...
.LP ãã®ã¯ã©ã¹ïŒãµãã¯ã©ã¹ã¯é€ãïŒã®å
šãŠã®ã€ã³ã¹ã¿ã³ã¹ããå¯èŠã«ãªããã»ãšãã©ã®å Žåãprivate[this]
ãšãããã ããã
private[this] val x: Int = ...
.LP ãã㧠x
ã®å¯èŠæ§ã¯ç¹å®ã®ã€ã³ã¹ã¿ã³ã¹ã«å¶éããããScala ã³ã³ãã€ã©ã¯ãprivate[this]
ãåçŽãªãã£ãŒã«ããžã®ã¢ã¯ã»ã¹ã«å€æã§ããïŒã¡ã³ããžã®ã¢ã¯ã»ã¹ããéçã«å®çŸ©ãããã¯ã©ã¹ã«éå®ãããããïŒãããã¯æã«ãæ§èœã®æé©åã«å¯äžããã
#### ã·ã³ã°ã«ãã³ã¯ã©ã¹å
Scala ã«ãããŠãã·ã³ã°ã«ãã³ã¯ã©ã¹åãçæããã®ã¯äžè¬çã ãäŸãã°ã
def foo() = new Foo with Bar with Baz {
...
}
.LP ãã®ãããªç¶æ³ã§å¯èŠæ§ãå¶éããã«ã¯ãæ»ãåã宣èšãã:
def foo(): Foo with Bar = new Foo with Bar with Baz {
...
}
.LP foo()
ã®åŒã³åºãåŽã¯ãè¿ãããã€ã³ã¹ã¿ã³ã¹ã®éå®ããããã¥ãŒ (Foo with Bar
) ãèŠãããšã«ãªãã
### æ§é çéšåå
éåžžãæ§é çéšåå (structural type^[蚳泚: "structural typing" ãçŽèš³ãããšâæ§é çãªåä»ãâã ããScalaã®æèã§ã¯âæ§é çéšåå(structural subtyping)âãšåãæå³ã ãšèããŠè¯ãããã®çšèªã®èæ¯ã«ã€ããŠã¯[ãã¡ãã®è§£èª¬](https://github.com/scalajp/effectivescala/pull/1#r455268)ãåç
§ããŠæ¬²ããã]) ã䜿ãã¹ãã§ã¯ãªããæ§é çéšååã¯äŸ¿å©ã§åŒ·åãªæ©èœã ããæ®å¿µãªããšã« JVM äžã§ã¯å¹ççã«å®è£
ãããªããããããå®è£
äžã®æ°ãŸããã«ãã£ãŠãæ§é çéšååã¯ãªãã¬ã¯ã·ã§ã³ã®ããã®ãšãŠãåªããç°¡æèšæ³ãæäŸããã
val obj: AnyRef
obj.asInstanceOf[{def close()}].close()
## ãšã©ãŒåŠç
Scala ã¯äŸå€æ©èœãæäŸããããæ£ç¢ºãã®ããã«ããã°ã©ããé©åã«å¯ŸåŠãã¹ãå Žåã«ããããäžè¬çãªãšã©ãŒã«å¯ŸããŠäœ¿ã£ãŠã¯ãããªãã代ããã« `Option` ã `com.twitter.util.Try` ã䜿ãã®ã¯ãæ
£ç¿çã§è¯ãéžæã ããããã¯ãåã·ã¹ãã ãå©çšããŠããŠãŒã¶ããšã©ãŒåŠçãé©åã«èæ
®ããããã«ããã
äŸãã°ãã¬ããžããªãèšèšããæã«ã以äžã®ãã㪠API ã«ããããªããããããªã:
trait Repository[Key, Value] {
def get(key: Key): Value
}
.LP ãããããããå®è£
ãããš key ãååšããªãæã«äŸå€ãæããå¿
èŠããããããè¯ãããæ¹ã¯ Option
ã䜿ãããšã :
trait Repository[Key, Value] {
def get(key: Key): Option[Value]
}
.LP ãã®ã€ã³ã¿ãã§ãŒã¹ãªããã¬ããžããªããããã key ãå«ãŸãªããŠãããããŸãããã°ã©ãã key ãèŠã€ãããªãå Žåã«å¯ŸåŠããªããã°ãªããªãããšãæ確ã«ãªããããã«ãOption
ã¯ããããã±ãŒã¹ã«å¯ŸåŠããããã®æ°å€ãã®ã³ã³ãããŒã¿ãåããŠãããäŸãã°ãgetOrElse
㯠key ãèŠã€ãããªãå Žåã«ããã©ã«ãå€ãäŸçµŠããã®ã«äœ¿ããã:
val repo: Repository[Int, String]
repo.get(123) getOrElse "defaultString"
### äŸå€åŠç
Scala ã®äŸå€æ©æ§ã¯éãã§ãã¯äŸå€ãã€ãŸãããã°ã©ããå¯èœæ§ã®ããäŸå€ãã«ããŒããŠããããã³ã³ãã€ã©ãéçã«ãã§ãã¯ã§ããªãã®ã§ãäŸå€åŠçã«ãããŠåºã網ããããããªããã¡ã ã
ããããããã€ãã® *fatal*ïŒèŽåœçïŒãªäŸå€ã¯ææ (catch) ããã¹ãã§ã¯ãªãã
However, some exceptions are *fatal* and should never be caught; the
code
try {
operation()
} catch {
case _ => ...
}
.LP ãã®ã³ãŒãã¯äŒæ¬ããã¹ãèŽåœçãªãšã©ãŒãææããŠããŸãã®ã§ãã»ãšãã©ã®å Žåã§èª€ãã ã代ããã«ãéèŽåœçãªäŸå€ã®ã¿ãææãã com.twitter.util.NonFatal
æœåºåã䜿ãã
try {
operation()
} catch {
case NonFatal(exc) => ...
}
## ã¬ããŒãžã³ã¬ã¯ã·ã§ã³
Twitter ã§ã¯ãå®éçšã«ãããŠå€ãã®æéãã¬ããŒãžã³ã¬ã¯ã·ã§ã³ã®ãã¥ãŒãã³ã°ã«è²»ããŠãããã¬ããŒãžã³ã¬ã¯ã·ã§ã³ã«ãããé¢å¿äºã¯ Java ã®ãããšã»ãšãã©åãã ããé¢æ°åã¹ã¿ã€ã«ã®å¯äœçšãšããŠãæ
£ç¿ç㪠Scala ã³ãŒã㯠Java ãããå€ãã®ïŒçåæéã®çãïŒã¬ããŒãžãçæãããHotSpot ã®äžä»£å¥ã¬ããŒãžã³ã¬ã¯ã·ã§ã³ã¯ãçåæéã®çãã¬ããŒãžãå¹ççã«è§£æŸããã®ã§ãã»ãšãã©ã®ç¶æ³ã§ã¯ããã¯åé¡ã«ãªããªãã
GC ã®æ§èœåé¡ã«åãçµãåã«ãTwitter ã§ã®ããã€ãã® GC ãã¥ãŒãã³ã°ã®çµéšã«ã€ããŠè§£èª¬ãã Attila ã®[ãã¬ãŒã³ããŒã·ã§ã³](http://www.infoq.com/presentations/JVM-Performance-Tuning-twitter)ãèŠãŠæ¬²ããã
GC åé¡ã軜æžããããã® Scala ã«ç¹æãªå¯äžã®æ段ã¯ãã¬ããŒãžã®çæãããå°ãªãããããšã ãããããããŒã¿ç¡ãã§è¡åããŠã¯ãªããªãïŒ æããã«æ§èœãå£åãããäœããããŠããã®ã§ãªããã°ãJava ã®æ§ã
ãªãããã¡ã€ãªã³ã°ããŒã«ã䜿ãããTwitter èªèº«ã [heapster](https://github.com/mariusaeriksen/heapster) ã
[gcprof](https://github.com/twitter/jvmgcprof) ãšãã£ãããŒã«ãæäŸããŠããã
## Java ãšã®äºææ§
Twitter ã§ã¯ãJava ãã䜿ãããã³ãŒãã Scala ã§æžããšããJava ã§ã®æ
£ç¿çãªäœ¿ãæ¹ãã§ããããã«ããŠãããå€ãã®å Žåãè¿œå ã®åªåã¯å¿
èŠãªããã¯ã©ã¹ãšçŽç²ãªïŒå®è£
ãå«ãŸãªãïŒãã¬ã€ãã¯ãJava ã«ãããŠå¯Ÿå¿ãããã®ãšæ£ç¢ºã«åããã®ã«ãªããããããæã
ãå¥åã® Java API ãæäŸããå¿
èŠããããã©ã€ãã©ãªã® Java API ã®æããã€ããè¯ãæ¹æ³ã¯ãJava ã§åäœãã¹ããæžãããšã ïŒã³ã³ãã€ã«ãéãã°è¯ãïŒããã®ãã¹ãã«ãã£ãŠã©ã€ãã©ãªã® Java ãã¥ãŒãå®å®ããŠããããšãä¿èšŒãããã®ã§ãå°æ¥ãScala ã³ã³ãã€ã©ãçæããå®è£
ãå€åããŠãæ€åºã§ããã
å®è£
ãå«ããã¬ã€ãã¯çŽæ¥ Java ããå©çšã§ããªãã代ããã«ãæœè±¡ã¯ã©ã¹ããã¬ã€ããšå
±ã«æ¡åŒµããã
// çŽæ¥ Java ããã¯å©çšã§ããªã
trait Animal {
def eat(other: Animal)
def eatMany(animals: Seq[Animal) = animals foreach(eat(_))
}
// ãããããããªãå©çšã§ãã
abstract class JavaAnimal extends Animal
## Twitterã®æšæºã©ã€ãã©ãª
Twitter ã«ãããŠãæãéèŠãªæšæºã©ã€ãã©ãªã¯ [Util](http://github.com/twitter/util) ãš [Finagle](https://github.com/twitter/finagle) ã ãUtil ã¯ãScala ã Java ã®æšæºã©ã€ãã©ãªã®æ¡åŒµãšããäœçœ®ä»ãã§ããããã«æ¬ ããŠããæ©èœãããé©åãªå®è£
ãæäŸãããFinagle ã¯ãTwitter ã® RPC ã·ã¹ãã ã§ãåæ£ã·ã¹ãã ã®æ§æèŠçŽ ã®äžæ žã ã
### Future
Future ã«ã€ããŠã¯ã䞊è¡æ§ã®ç« ã§ãç°¡åã«è°è«ãããFuture ã¯éåæåŠçã®å調ã«ãããŠäžå¿çãªæ©æ§ã§ãTwitter ã®ã³ãŒãããŒã¹ã Finagle ã®ã³ã¢ã§åºã䜿ãããŠãããFuture ã¯äžŠè¡ã€ãã³ãã®åæãå¯èœã«ãããšãšãã«ã䞊è¡æ§ã®é«ãæäœã«ã€ããŠã®æšè«ãåçŽåããããŸã Future ã䜿ããšãJVM äžã§äžŠè¡æäœãéåžžã«å¹ççã«å®è£
ã§ããã
Twitter ã® Future ã¯*éåæ*ã ãã ãããäŸãã°ãããã¯ãŒã¯å
¥åºåããã£ã¹ã¯å
¥åºåã®ããã«ãã¹ã¬ããã®å®è¡ãäžæåæ¢ããããããããã³ã°æäœã¯ã·ã¹ãã ãåŠçããå¿
èŠãããããã®ãšããã·ã¹ãã ã¯ããããã³ã°æäœã®çµæã«å¯Ÿãã Future ãæäŸãããFinagle ã¯ããããã¯ãŒã¯å
¥åºåã®ããã®ããããã·ã¹ãã ãæäŸããã
Future ã¯åçŽæçã : Future ã¯ããŸã å®äºããŠããªãèšç®ã®çµæã*çŽæ (promise)* ãããFuture ã¯åçŽãªã³ã³ããïŒãã¬ãŒã¹ãã«ãïŒã ããã¡ãããèšç®ã¯å€±æããããšãããã®ã§ããã®å€±æããšã³ã³ãŒãããå¿
èŠããããFuture ã¯ã*ä¿çäž (pending)*ã*倱æ (failed)*ã*å®äº (completed)* ã®äžã€ã®ç¶æ
ã®ãã¡ããã äžã€ãåãããšãã§ããã
ããäžåºŠç¢ºèªãããšãåæ (composition) ãšã¯ãåçŽãªã³ã³ããŒãã³ããçµåããŠããè€éãªã³ã³ããŒãã³ãã«ããããšã ãé¢æ°åæã¯ãåæã®æšæºçãªäŸã : é¢æ° f ãš g ãäžãããããšããåæé¢æ° (gâf)(x) = g(f(x)) ã¯ããŸã x ã f ã«é©çšããŠããã®çµæã g ã«é©çšããçµæã ããã®åæé¢æ°ã Scala ã§æžããš:
val f = (i: Int) => i.toString
val g = (s: String) => s+s+s
val h = g compose f // : Int => String
scala> h(123)
res0: java.lang.String = 123123123
{ i => i.toString }
ã¯ãæŽæ°å€ i
ãå©çšå¯èœã«ãªããŸã§åŒã³åºãããªãããŸããå€æãããã³ã¬ã¯ã·ã§ã³ resultStr
ããã®æãŸã§ä¿çç¶æ
ã«ãªãã
ãªã¹ãã¯å¹³åŠå (flatten) ã§ãã;
val listOfList: List[List[Int]] = ...
val list: List[Int] = listOfList.flatten
.LP åæ§ã«ãFuture ã«ãããŠãå¹³åŠåã¯æå³ããªã:
val futureOfFuture: Future[Future[Int]] = ...
val future: Future[Int] = futureOfFuture.flatten
.LP Future ã¯é
延ããã®ã§ãflatten
ã®å®è£
ã¯ïŒçŽã¡ã«ïŒFuture ãè¿ãå¿
èŠãããããã® Future ã¯ãå€åŽã® Future(Future[Future[Int]]
) ãå®äºããŠããã®ããšã«å
åŽã® Future(Future[Future[Int]]
) ãå®äºããã®ãåŸ
ã£ãŠããçµæã ãããå€åŽã® Future ã倱æããããå¹³åŠåããã Future ã倱æããå¿
èŠãããã
Future ã¯ãList ãšåæ§ã« `flatMap` ãå®çŸ©ããŠããã`Future[A]` ãå®çŸ©ããã·ã°ããã£ã¯ã
flatMap[B](f: A => Future[B]): Future[B]
.LP ãã㯠map
ãš flatten
ã®çµã¿åããããããªãã®ã§ããã®ããã«å®è£
ãããšä»¥äžã®ããã«ãªã:
def flatMap[B](f: A => Future[B]): Future[B] = {
val mapped: Future[Future[B]] = this map f
val flattened: Future[B] = mapped.flatten
flattened
}
ããã¯åŒ·åãªçµã¿åããã ïŒ `flatMap` ã䜿ããšãäºã€ã® Future ãé çªã«å®è¡ããçµæã§ãã Future ãå®çŸ©ã§ãããããã¯ãäžã€ç®ã® Future ã®çµæã«åºã¥ããŠèšç®ãããäºã€ç®ã® Future ã ããŠãŒã¶ (ID) ã®èªèšŒã®ããã«ãäºã€ã® RPC ãè¡ãå¿
èŠããããšæ³åããããåæãããæäœã¯ä»¥äžã®æ¹æ³ã§å®çŸ©ã§ãã:
def getUser(id: Int): Future[User]
def authenticate(user: User): Future[Boolean]
def isIdAuthed(id: Int): Future[Boolean] =
getUser(id) flatMap { user => authenticate(user) }
.LP ããããçš®é¡ã®çµåã®ããäžã€ã®æ©æµã¯ããšã©ãŒåŠçãçµã¿èŸŒã¿ã«ãªã£ãŠããããšã : getUser(..)
ã authenticate(..)
ãããã«ãšã©ãŒåŠçãããªãéããisIdAuthed(..)
ãè¿ã Future ã¯å€±æããã
#### ã¹ã¿ã€ã«
Future ã®ã³ãŒã«ããã¯ã¡ãœãã (`respond`, `onSuccess`, `onFailure`, `ensure`) ã¯ããã®èŠªã«*é£éãã*æ°ã㪠Future ãè¿ãããã® Future ã¯ã芪ãå®äºããåŸã§ã®ã¿å®äºããããšãä¿èšŒãããŠããããã®ãã¿ãŒã³ãå®çŸããã«ã¯ã
acquireResource() onSuccess { value =>
computeSomething(value)
} ensure {
freeResource()
}
.LP ãã®ãšã freeResource()
㯠computeSomething
ã®åŸã§ã®ã¿å®è¡ãããããšãä¿èšŒããããããã«ããããã€ãã£ã㪠try .. finally
ãã¿ãŒã³ã®ãšãã¥ã¬ãŒããå¯èœã«ããã
`foreach` ã®ä»£ããã« `onSuccess` ã䜿ããã`onSuccess` ã®æ¹ãã`onFailure` ãšå¯Ÿç§°çã§ç®çãããè¯ãè¡šãååã ããé£éãå¯èœã ã
`Promise` ã€ã³ã¹ã¿ã³ã¹ãçŽæ¥äœãã®ã¯ãã€ã§ãé¿ããããã«ããã: ã»ãšãã©ã®ããããã¿ã¹ã¯ã¯ãå®çŸ©æžã¿ã®ã³ã³ãããŒã¿ã䜿ãããšã§éæã§ããããããã®ã³ã³ãããŒã¿ã¯ããšã©ãŒããã£ã³ã»ã«ã®äŒæãä¿èšŒããããŸããã³ã³ãããŒã¿ã¯äžè¬çã«*ããŒã¿ãããŒã»ã¹ã¿ã€ã«*ã®ããã°ã©ãã³ã°ãä¿é²ããããã«ããæ®æ®µã¯åæåã `volatile` 宣èšãäžèŠã«ãªãã
æ«å°Ÿååž°æ¹åŒã§æžãããã³ãŒãã¯ã¹ã¿ãã¯ç©ºéã®ãªãŒã¯ãåŒãèµ·ãããªãã®ã§ãããŒã¿ãããŒã»ã¹ã¿ã€ã«ã§ã«ãŒããå¹ççã«å®è£
ã§ãã:
case class Node(parent: Option[Node], ...)
def getNode(id: Int): Future[Node] = ...
def getHierarchy(id: Int, nodes: List[Node] = Nil): Future[Node] =
getNode(id) flatMap {
case n@Node(Some(parent), ..) => getHierarchy(parent, n :: nodes)
case n => Future.value((n :: nodes).reverse)
}
`Future` ã¯æçšãªã¡ãœãããããããå®çŸ©ããŠããã`Future.value()` ã `Future.exception()` ã䜿ããšãäºåã«çµæãæºãããã Future ãäœããã`Future.collect()`, `Future.join()`, `Future.select()` ã¯ãè€æ°ã® Future ãäžã€ã«ãŸãšããã³ã³ãããŒã¿ãæäŸããïŒã€ãŸã scatter-gather æäœã® gather éšåïŒã
#### ãã£ã³ã»ã«
Future ã¯åŒ±ããã£ã³ã»ã«ãå®è£
ããŠããã`Future#cancel` ã®åŒã³åºãã¯ãçŽã¡ã«èšç®ãçµäºããã代ããã«ãã©ããæçµçã« Future ãæºãããããã»ã¹ããªã®ãåãåãããããšãã§ãã*ã·ã°ãã«*ãã¬ãã«ããªã¬ã§äŒæããããã£ã³ã»ã«ã¯ãå€ãšã¯å察æ¹åãžäŒæãã: ã³ã³ã·ã¥ãŒã (consumer) ãã»ãããããã£ã³ã»ã«ã»ã·ã°ãã«ã¯ãããã¥ãŒãµ (producer) ãžãšäŒæããããããã¥ãŒãµã¯ `Promise` ã® `onCancellation` ã䜿ã£ãŠãã·ã°ãã«ã«å¿ããŠäœåãããªã¹ããŒãæå®ããã
ã€ãŸãããã£ã³ã»ã«ã®åäœã¯ãããã¥ãŒãµã«äŸåããããããã©ã«ãã®å®è£
ã¯ååšããªãã*ãã£ã³ã»ã«ã¯ãã³ãã«éããªãã*
#### Local
Util ã©ã€ãã©ãªã® [`Local`](https://github.com/twitter/util/blob/master/util-core/src/main/scala/com/twitter/util/Local.scala#L40) ã¯ãç¹å®ã® Future ã®ãã£ã¹ãããããªãŒã«å¯ŸããããŒã«ã«ãªåç
§ã»ã«ãæäŸããã`Local` ã«å€ãã»ãããããšãåãã¹ã¬ããå
ã® Future ã«ãã£ãŠé
延ãããããããèšç®ããã®å€ãå©çšã§ããããã«ãªãããããã¯ã¹ã¬ããããŒã«ã«ã«äŒŒãŠãããããã®ã¹ã³ãŒãã Java ã¹ã¬ããã§ãªãâFuture ã¹ã¬ããâã®ããªãŒã§ããç¹ãç°ãªãã
trait User {
def name: String
def incrCost(points: Int)
}
val user = new Local[User]
...
user() = currentUser
rpc() ensure {
user().incrCost(10)
}
.LP ãã㧠ensure
ãããã¯å
ã® user()
ã¯ãã³ãŒã«ããã¯ãè¿œå ãããæç¹ã§ã®ããŒã«ã«ãª user
ã®å€ãåç
§ããã
ã¹ã¬ããããŒã«ã«ãšåæ§ã« `Local` ã¯éåžžã«äŸ¿å©ãªããšãããããã»ãšãã©ã®å Žåã¯é¿ããã¹ãã : ããšãããããæ¹ãè² æ
ãå°ãªããšãã§ããããŒã¿ãæ瀺çã«æž¡ããŠåãæ¹æ³ã§ã¯åé¡ãååã«è§£æ±ºã§ããªãããšã確èªãããã
Local ã¯ãã³ã¢ã©ã€ãã©ãªã«ããã*éåžžã«*äžè¬çãªé¢å¿äºã®ããã«å¹æçã«äœ¿ããã: äŸãã°ãRPC ã®ãã¬ãŒã¹ã䜿ã£ãã¹ã¬ãã管çãã¢ãã¿ã®äŒæãFuture ã³ãŒã«ããã¯ã®ããã®âã¹ã¿ãã¯ãã¬ãŒã¹âã®äœæãªã©ããã®ä»ã®è§£æ±ºçã§ã¯ãŠãŒã¶ã«é床ãªè² æ
ãããå Žåã ããã®ä»ã®ã»ãšãã©ã®ç¶æ³ã§ Local ã¯äžé©åã ã
### Offer ãš Broker
䞊è¡ã·ã¹ãã ã¯éåžžã«è€éã ãããã¯ãå
±æããŒã¿ããªãœãŒã¹ãžã®ã¢ã¯ã»ã¹ãå調ãããå¿
èŠãããããã ã[ã¢ã¯ã¿ãŒ (Actor)](http://doc.akka.io/api/akka/current/index.html#akka.actor.Actor) ã¯ã䞊è¡ã·ã¹ãã ãåçŽã«ããäžã€ã®æŠç¥ãæèµ·ããŠãããã¢ã¯ã¿ãŒã¯é次çãªããã»ã¹ã§ãããããã®ã¢ã¯ã¿ãŒãèªåèªèº«ã®ç¶æ
ããªãœãŒã¹ãä¿æããã¡ãã»ãŒãžã³ã°ã«ãã£ãŠä»ã®ã¢ã¯ã¿ãŒãšããŒã¿ãå
±æãããå
±æããŒã¿ã¯ã¢ã¯ã¿ãŒéã§éä¿¡ããå¿
èŠãããã
Offer ãš Broker ã¯ãããã«åºã¥ããŠäžã€ã®éèŠãªèãæ¹ãåãå
¥ããŠãããäžã€ç®ã¯ãéä¿¡ãã£ãã« (Broker) ã第äžçŽ (first class) ã§ããããšãããªãã¡ãã¢ã¯ã¿ãŒã«çŽæ¥ã¡ãã»ãŒãžãéãã®ã§ã¯ãªã Broker çµç±ã§éä¿¡ãããäºã€ç®ã¯ãOffer ã Broker ãåæåã¡ã«ããºã ã§ããããš: éä¿¡ããããšã¯åæåããããšã ããã®æå³ã¯ãBroker ã¯å調ã¡ã«ããºã ãšããŠäœ¿ãããšããããšã : ããã»ã¹ `a` ãããã»ã¹ `b` ã«ã¡ãã»ãŒãžãéä¿¡ãããšãã`a` ãš `b` ã¯å
±ã«ã·ã¹ãã ã®ç¶æ
ã«ã€ããŠåæãããäžã€ç®ã¯ãéä¿¡ã*éžæçã«*å®è¡ã§ããããš: äžã€ã®ããã»ã¹ã¯ããã€ãç°ãªãéä¿¡ãææ¡ã§ãããããã®ãã¡ãã äžã€ãæå¹ã«ãªãã
äžè¬çãªïŒä»ã®åæãšåæ§ã®ïŒããæ¹ã§éžæçãªéä¿¡ããµããŒãããã«ã¯ãéä¿¡ã®è¡çº (act of communicating) ããéä¿¡ã®èšè¿° (description of a communication) ãåé¢ããå¿
èŠãããããããããã®ã `Offer` ã ãOffer ã¯éä¿¡ãèšè¿°ããæ°žç¶çãªå€ã§ãïŒOffer ã«äœçšããïŒéä¿¡ãå®è¡ããã«ã¯ Offer ã® `sync()` ã¡ãœããã§åæåããã
trait Offer[T] {
def sync(): Future[T]
}
.LP `sync()` ã¯ãéä¿¡ãå€ãåŸããšãã«ã亀æãããå€ãçæãã Future[T]
ãè¿ãã
`Broker` ã¯éä¿¡ã®ãã£ãã«ã§ãããOffer ã䜿ã£ãŠå€ã®äº€æãå調ãã:
trait Broker[T] {
def send(msg: T): Offer[Unit]
val recv: Offer[T]
}
.LP ãããŠãäºã€ã® Offer ãçæãããšãã
val b: Broker[Int]
val sendOf = b.send(1)
val recvOf = b.recv
.LP sendOf
ãš recvOf
ã¯ã©ã¡ããåæåãããŠããã
// In process 1:
sendOf.sync()
// In process 2:
recvOf.sync()
.LP äž¡æ¹ã® Offer ãå€ãåŸãŠ 1
ã®å€ã亀æãããã
éžæçãªéä¿¡ã¯ã`Offer.choose` ã§ããã€ãã® Offer ãçµåããããšã§è¡ãããã
def choose[T](ofs: Offer[T]*): Offer[T]
.LP ã¯æ°ãã Offer ãçæãããããã¯ãåæåãããšãofs
ã®ãã¡æåã«å©çšå¯èœã«ãªã£ããã®ãå¯äžã€ååŸãããããã€ããå³åº§ã«å©çšå¯èœã«ãªã£ãå Žåã¯ãååŸãã `Offer` ã¯ã©ã³ãã ã«éžã°ããã
`Offer` ãªããžã§ã¯ãã¯ãBroker ããåŸã Offer ãšçµã¿åãããŠäœ¿ãã¯ã³ãªãã® Offer ãããããæã£ãŠããã
Offer.timeout(duration): Offer[Unit]
.LP ã¯äžããããæéã®åŸã«èµ·åãã Offer ã ãOffer.never
ã¯æ±ºããŠå€ãååŸããªããäžæ¹ãOffer.const(value)
ã¯ãäžããããå€ãçŽã¡ã«ååŸããããããã¯ãéžæçãªéä¿¡ã«ãã£ãŠåæããã®ã«ãæçšã ãäŸãã°ãéä¿¡æäœã«ã¿ã€ã ã¢ãŠããé©çšããã«ã¯:
Offer.choose(
Offer.timeout(10.seconds),
broker.send("my value")
).sync()
Offer ãš Broker ã䜿ãæ¹æ³ãš [SynchronousQueue](http://docs.oracle.com/javase/jp/6/api/java/util/concurrent/SynchronousQueue.html) ãæ¯ã¹ãŠã¿ãããªãããäž¡è
ã«ã¯åŸ®åŠã ãéèŠãªéãããããOffer ã¯ããããããã¥ãŒã§ã¯ãšãŠãã§ããªããããªæ¹æ³ã§çµã¿ç«ãŠãããšãã§ãããäŸãã°ãBroker ã§è¡šããäžé£ã®ãã¥ãŒãèãã:
val q0 = new Broker[Int]
val q1 = new Broker[Int]
val q2 = new Broker[Int]
.LP ããã§ãèªã¿èŸŒã¿ã®ããã®ããŒãžããããã¥ãŒãäœã£ãŠã¿ã:
val anyq: Offer[Int] = Offer.choose(q0.recv, q1.recv, q2.recv)
.LP anyq
㯠Offer ã§ãæåã«å©çšå¯èœã«ãªã£ããã¥ãŒããèªã¿èŸŒãããªãããã® anyq
ã¯ãã¯ãåæçã§ãããå
éšã«ãããã¥ãŒã®åäœãå©çšã§ãããããããåæã¯ããã¥ãŒã䜿ãæ¹æ³ã§ã¯ãšãŠãäžå¯èœã ã
#### äŸ: ç°¡åãªã³ãã¯ã·ã§ã³ããŒã«
ã³ãã¯ã·ã§ã³ããŒã«ã¯ããããã¯ãŒã¯ã¢ããªã±ãŒã·ã§ã³ã§ã¯äžè¬çã§ããããŠãã¯å®è£
ãã«ãããäŸãã°ãåã
ã®ã¯ã©ã€ã¢ã³ãã¯ç°ãªãã¬ã€ãã³ã·ãèŠæ±ãããããããŒã«ããã®ååŸã«ã¿ã€ã ã¢ãŠããããã®ãå€ãã®å Žåã§æãŸãããããŒã«ã¯åççã«ã¯åçŽã : ã³ãã¯ã·ã§ã³ã®ãã¥ãŒãä¿æããåŸ
æ©ã¯ã©ã€ã¢ã³ã (waiter) ãå
¥ã£ãŠãããæºãããŠãããåŸæ¥ã®åæåããªããã£ãã§ã¯ãå
žåçã«ã¯äºã€ã®ãã¥ãŒã䜿ããäžã€ã¯ waiters ã§ãã³ãã¯ã·ã§ã³ (connection) ããªãæã«äœ¿ããããããäžã€ã¯ connections ã§ãããã¯åŸ
æ©ã¯ã©ã€ã¢ã³ã (waiter) ããªãæã«äœ¿ãããã
Offer ãš Broker ã䜿ããšãããããšãŠãèªç¶ã«è¡šçŸã§ãã:
class Pool(conns: Seq[Conn]) {
private[this] val waiters = new Broker[Conn]
private[this] val returnConn = new Broker[Conn]
val get: Offer[Conn] = waiters.recv
def put(c: Conn) { returnConn ! c }
private[this] def loop(connq: Queue[Conn]) {
Offer.choose(
if (connq.isEmpty) Offer.never else {
val (head, rest) = connq.dequeue
waiters.send(head) { _ => loop(rest) }
},
returnConn.recv { c => loop(connq enqueue c) }
).sync()
}
loop(Queue.empty ++ conns)
}
`loop` ã¯ãã³ãã¯ã·ã§ã³ãè¿åŽãããç¶æ
ã«ããããšãåžžã«ãªãã¡ãŒ (offer) ãããšå
±ã«ããã¥ãŒã空ã§ãªãå Žåã®ã¿éä¿¡ããªãã¡ãŒãããæ°žç¶çãªãã¥ãŒã䜿ãããšã§æšè«ãããåçŽã«ã§ãããããŒã«ã®ã€ã³ã¿ãã§ãŒã¹ã«ã Offer ã䜿ã£ãŠããã®ã§ãåŒã³åºãåŽã¯ã³ã³ãããŒã¿ã䜿ãããšã§ã¿ã€ã ã¢ãŠããé©çšã§ãã:
val conn: Future[Option[Conn]] = Offer.choose(
pool.get { conn => Some(conn) },
Offer.timeout(1.second) { _ => None }
).sync()
ã¿ã€ã ã¢ãŠããå®è£
ããã®ã«ãã以äžã®ç°¿èšã¯å¿
èŠãªãããã㯠Offer ã®åäœã«ãããã®ã : ãã `Offer.timeout` ãéžæããããããã¯ãããŒã«ããã®åä¿¡ããªãã¡ãŒããªããã€ãŸããããŒã«ãšåŒã³åºãåŽã `waiters` Broker äžã§ã®éä¿¡ãšåä¿¡ãããããåæã«åæããããšã¯ãªãã
#### äŸ: ãšã©ãã¹ããã¹ã®ç¯©
䞊è¡ããã°ã©ã ããåæçã«éä¿¡ããäžé£ã®é次çãªããã»ã¹ãšããŠæ§ç¯ããã®ã¯å€ãã®å Žåã§æçšã ããå Žåã«ãã£ãŠã¯ããã°ã©ã ãéåžžã«åçŽåã§ãããOffer ãš Broker ã¯ããããåçŽåãçµ±äžåããæ段ãæäŸãããå®éããããã®ã¢ããªã±ãŒã·ã§ã³ã¯ã人ã«ãã£ãŠã¯âå€å
žçãªâ䞊è¡æ§ã®åé¡ãšããŠèãããããããªããã®ãä¹ãè¶ããããµãã«ãŒãã³ãã¯ã©ã¹ãã¢ãžã¥ãŒã«ãšåãããã«ãïŒOffer ã Broker ãçšããïŒäžŠè¡ããã°ã©ãã³ã°ã¯æçšãª*æ§é å*ããŒã«ã ãããã¯ãå¶çŽå
足åé¡ (Constraint Satisfaction Problem; CSP) ããã®ããäžã€ã®éèŠãªã¢ã€ãã¢ã ã
ããã®äžã€ã®äŸã¯[ãšã©ãã¹ããã¹ã®ç¯©](http://ja.wikipedia.org/wiki/%E3%82%A8%E3%83%A9%E3%83%88%E3%82%B9%E3%83%86%E3%83%8D%E3%82%B9%E3%81%AE%E7%AF%A9)ã§ãæŽæ°ã¹ããªãŒã ã«å¯Ÿãããã£ã«ã¿ã®é£ç¶çãªé©çšãšããŠæ§é åã§ããããŸããæŽæ°ã®çææºãå¿
èŠã :
def integers(from: Int): Offer[Int] = {
val b = new Broker[Int]
def gen(n: Int): Unit = b.send(n).sync() ensure gen(n + 1)
gen(from)
b.recv
}
.LP integers(n)
ã¯ãåã« n
ããå§ãŸãå
šãŠã®é£ç¶ããæŽæ°ã® Offer ã ã次ã«ããã£ã«ã¿ãå¿
èŠã :
def filter(in: Offer[Int], prime: Int): Offer[Int] = {
val b = new Broker[Int]
def loop() {
in.sync() onSuccess { i =>
if (i % prime != 0)
b.send(i).sync() ensure loop()
else
loop()
}
}
loop()
b.recv
}
.LP filter(in, p)
ã¯ãin
ããçŽ æ° p
ã®åæ°ãåãé€ã Offer ãè¿ããæåŸã«ã篩 (sieve) ãå®çŸ©ãã:
def sieve = {
val b = new Broker[Int]
def loop(of: Offer[Int]) {
for (prime <- of.sync(); _ <- b.send(prime).sync())
loop(filter(of, prime))
}
loop(integers(2))
b.recv
}
.LP loop()
ã®åäœã¯åçŽã : of
ãã次ã®çŽ æ°ãèªã¿åãããã®çŽ æ°ãé€ãã of
ã«ãã£ã«ã¿ãé©çšãããloop
ãååž°ããã«ã€ããŠé£ç¶ããçŽ æ°ããã£ã«ã¿ããã篩ãæã«å
¥ããããã§ãæåã® 10000 åã®çŽ æ°ãåºåã§ãã:
val primes = sieve
0 until 10000 foreach { _ =>
println(primes.sync()())
}
ãã®ã¢ãããŒãã¯ã篩ãåçŽãã€çŽäº€ããã³ã³ããŒãã³ããžãšæ§é åã§ããã ãã§ãªããã¹ããªãŒã ãšããŠæ±ãã: åã¯ãèå³ãããçŽ æ°ã®éåãäºåã«èšç®ããå¿
èŠããªãããã£ããã¢ãžã¥ã©ãªãã£ãæ¡åŒµã§ããã
## è¬èŸ
æ¬ã¬ãã¹ã³ã¯ãTwitter 瀟㮠Scala ã³ãã¥ããã£ã«ãããã®ã ãç§ã¯èª å®ãªèšé²è
ã§ããããã
Blake Matheny ãš Nick KallenãSteve Guryããã㊠Raghavendra Prabhu ã«ã¯ããšãŠãæçãªå©èšãšå€ãã®åªããææ¡ãäžããŠããã£ãã
### æ¥æ¬èªçãžã®è¬èŸ
æ¬ããã¥ã¡ã³ãã®æ¥æ¬èªèš³ã¯ã[@okapies](http://github.com/okapies) ãš [@scova0731](https://github.com/scova0731) ãæ
åœããŸããã
翻蚳ã«ããã£ãŠã¯ãæ¥æ¬ã® Scala ã³ãã¥ããã£ããæ°å€ãã®è²¢ç®ãé ããŸãã: [@xuwei-k](http://github.com/xuwei-k) ããã[@kmizu](http://github.com/kmizu) ããã[@eed3si9n](http://github.com/eed3si9n) ããã[@akr4](http://github.com/akr4) ããã[@yosuke-furukawa](http://github.com/yosuke-furukawa) ãããm hanada ãããããã³[æ¥æ¬ Scala ãŠãŒã¶ãŒãºã°ã«ãŒã](http://jp.scala-users.org/)ã®çãããïŒä»¥äžãé äžåïŒ
ãŸãã[@kmizu](http://github.com/kmizu) ãããš[@eed3si9n](http://github.com/eed3si9n) ããã«ã¯ãé«åºŠã«å°éçãªè°è«ã«ã€ããŠè²Žéãªå©èšãé ããŸããã
ããããšãããããŸãã
[Scala]: http://www.scala-lang.org/
[Finagle]: http://github.com/twitter/finagle
[Util]: http://github.com/twitter/util