Scalaでも値が範囲に入っているか確認したい

はじめにとおわりに

Scalaで整数が特定の範囲に入っているかを宣言的に書けないかなと思ったのですが、やっぱり書けました。

val range = 0 to 255
println(range contains -1)
println(range contains 0)
println(range contains 20)
println(range contains 255)
println(range contains 256)

val range2 = 0 until 255
println(range2 contains 254)
println(range2 contains 255)
false
true
true
true
false
true
false

これだけです。

おわり

Akkaのエラーハンドリングストラテジを確認したい

はじめに

Akkaを実運用に突っ込もうと考えたときに一番最初に気になるのはActorがくたばった時の挙動ですよね。 知らない間にActorがくたばっていてメッセージだけが虚空の彼方に消えていったなんて事態になった日には次回の自身のボーナスも虚空に消えかねません。

ボーナスを死守するためにもエラーハンドリングは必須の知識として学ぶべきです。

サンプルコード

今回はこんな感じのサンプルコードを使用します。

package org.jyuch

import akka.actor.SupervisorStrategy.{Escalate, Restart, Resume, Stop}
import akka.actor.{Actor, ActorSystem, OneForOneStrategy, Props}

import scala.concurrent.duration._

import scala.language.postfixOps

object Hello {
  def main(args: Array[String]): Unit = {
    val system = ActorSystem()
    val calc = system.actorOf(CalcActor.props())

    calc ! Add(1, 2)
    calc ! Add(-1, 2)
    calc ! Add(1, 2)
    calc ! Subtract(3, 2)

    io.StdIn.readLine()
    system.terminate()
  }
}

trait OperandActor extends Actor {
  override def postRestart(reason: Throwable): Unit = {
    super.postRestart(reason)
    //println(reason.getClass.getTypeName)
  }
}

class CalcActor extends OperandActor {
  val add = context.actorOf(AddActor.props(), "add")
  val subtract = context.actorOf(SubtractActor.props(), "subtract")

  def receive = {
    case Result(v) =>{
      println(this + " : " + v)
    }
    case a: Add => add ! a
    case s: Subtract => subtract ! s
  }

  override val supervisorStrategy =
    OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
      case _: Exception => Stop
    }
}

object CalcActor {
  def props(): Props = Props[CalcActor]
}

class AddActor extends OperandActor {
  def receive = {
    case Add(x, y) => {
      println(this)
      if (x < 0 || y < 0) throw new Exception()
      sender() ! Result(x + y)
    }
  }
}

object AddActor {
  def props() = Props[AddActor]
}

class SubtractActor extends OperandActor {
  def receive = {
    case Subtract(x, y) => {
      println(this)
      if (x - y < 0) throw new Exception()
      sender() ! Result(x - y)
    }
  }
}

object SubtractActor {
  def props() = Props[SubtractActor]
}

sealed abstract class Operand

case class Add(x: Int, y: Int) extends Operand

case class Subtract(x: Int, y: Int) extends Operand

case class Result(value: Int)

子Actorの後始末

エラーハンドリングストラテジは親ActorのsupervisorStrategyで制御されますが、何も指定しない(デフォルト)とくたばったActorだけを再起動します。

supervisorStrategyで指定できるストラテジは以下の感じになります。

ストラテジ 意味
OneForOneStrategy 子Actorがくたばったらそいつだけ何とかする
AllForOneStrategy 子Actorがくたばったらすべての子Actorを何とかする

で、何とかする内容が以下の感じです。

ストラテジ 意味
Escalate 上位のActorに判断を委譲する
Restart 子Actorを再起動する
Resume エラーを気にせず続行する
Stop 子Actorを停止する

そして、ストラテジをこんな感じに指定します。

import akka.actor.OneForOneStrategy
import akka.actor.SupervisorStrategy._
import scala.concurrent.duration._

override val supervisorStrategy =
  OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
    case _: ArithmeticException      ⇒ Resume
    case _: NullPointerException     ⇒ Restart
    case _: IllegalArgumentException ⇒ Stop
    case _: Exception                ⇒ Escalate
  }

ガーディアンアクター

上記の指定で子がくたばった時の挙動を制御できるようになりました。じゃあ自身がくたばったらどうなるのさ?

自身がほかのActorの子だったらそいつが決めますが、自身がsystem.actorOfが生成されていた場合は自身の命運はガーディアンアクターが決めます。

doc.akka.io

Akka 2.1以降では設定ファイルのakka.actor.guardian-supervisor-strategyでガーディアンアクターのストラテジを設定出来ます。

doc.akka.io

  actor {

    # Either one of "local", "remote" or "cluster" or the
    # FQCN of the ActorRefProvider to be used; the below is the built-in default,
    # note that "remote" and "cluster" requires the akka-remote and akka-cluster
    # artifacts to be on the classpath.
    provider = "local"

    # The guardian "/user" will use this class to obtain its supervisorStrategy.
    # It needs to be a subclass of akka.actor.SupervisorStrategyConfigurator.
    # In addition to the default there is akka.actor.StoppingSupervisorStrategy.
    guardian-supervisor-strategy = "akka.actor.DefaultSupervisorStrategy"

現状ではakka.actor.StoppingSupervisorStrategyakka.actor.DefaultSupervisorStrategyが指定できるようです。

おわりに

これならActorがくたばったときにいい感じにActorを再起動できそうです。

これでボーナスを心配することなく安心して今夜も眠ることが出来そうです。

おわり

github.com

Scalaでも演算子のオーバーロードをしたい

はじめに

Scala演算子オーバーロードについてサクッと確認してみます。

オーバーロード

まぁ、とりあえず言語仕様を見ておけば安心だよねといった感じです。困ったら公式ドキュメントを読む。日本書紀にもそう書いてあります。

case class AltInt(val value: Int) {
  def +(x: AltInt) = plus(x)
  def plus(x: AltInt) = AltInt(value + x.value)
  def unary_~ = AltInt(value * -1)
  def +:(x: Int) = AltInt(value + x)
  def -:(x: AltInt) = { println(s"$value - ${x.value} = ${value - x.value}"); AltInt(value - x.value) }
  def *(x: AltInt) = AltInt(x.value * value)
  def ! = println(value)
}

中置演算子

Scalaでは引数を一つ取るメソッドはすべて中置演算子として扱えます。 逆に+のような演算子も普通*1のメソッドのように呼び出せます。

val a = AltInt(10)
val b = AltInt(20)

println(a.plus(b))
println(a plus b)
println(a.+(b))
println(a + b)

なお、優先順位はメソッドの先頭の文字によって規定されるようです。

先頭の文字 優先度
(all letters) 低い
|
^
&
= !
< >
:
+ -
* / %
(all other special characters) 高い
val a = AltInt(10)
val b = AltInt(20)
println(a * a + b) // 120

また、メソッドが:で終わる場合は引数とレシーバの位置関係が逆転します。

println(1 +: a)

また、通常演算子は左結合ですがこの場合は右結合になります。

println(b -: a -: a)
10 - 10 = 0
0 - 20 = -20
AltInt(-20)

後置演算子

中置演算子と同じように引数を取らないメソッドは後置演算子として呼び出せます。

println(a!)
println(a.!)

前置演算子

前置演算子unary_記号で定義できますが、記号に入ることができるのは+-!~に限られます。

println(~a)
println(a.unary_~)

おわりに

素直でいいですね。

*1:普通?普通ってなんだ?