Code Snippets | bearsworld

bearsworld

for friendbear GitHub Pages.

Follow me on GitHub

Code Snippets

01 Oct 2018 - friendbear

Scala Snippets

https://scastie.scala-lang.org

Scala language

  • async
import scala.concurrent._
import java.util.concurrent.Executors
val ec = ExecutionContext.fromExecutorService(Executors.newFixedThreadPool(1))
//(1 to 30).foreach {_ => Future{Thread.sleep(3000); println(new java.util.Date)}(ec)}
while(true) {
  Future {
    Thread.sleep(5);
    println("hogehoge");
  }(ec)
}


import scala.concurrent._
import java.util.concurrent.Executors

object Main extends App{

  val ec = ExecutionContext.fromExecutorService(Executors.newFixedThreadPool(1))
  //(1 to 30).foreach {_ => Future{Thread.sleep(3000); println(new java.util.Date)}(ec)}
  while(true) {
    Future {
      Thread.sleep(5);
      println("hogehoge");
    }(ec)
  }
  }
  • Exception1
/**
 * allCatch optでOptionを戻り値に
 *
 * とりあえずallCatchですべての例外をcatchする処理を書く。
 * 最も手軽に使えそうなのがopt。例外だったらNone、成功すればSome(x)を返す
 */
import scala.util.control.Exception._
 
def divide(x: Int, y: Int) = allCatch opt { x / y }
  var result = divide(0, 0)
  println(result)
  //#=> None

  result = divide(0, 3)
  println(result)
  //#=> Some(0)
  • Exception2
/**
 * allCatch either/withTryでEitherやSuccess/Failureを戻り値に
 * eitherでLeftとRightの値が返るようにしてみる。
 */
import scala.util.control.Exception._

def divide(x: Int, y: Int) = allCatch either { x / y }

divide(0, 0) match {
  case Left(e)  => e.printStackTrace()
  case Right(i) => println(i)
}

// withTryを使うとSuccess/Failureで返る。
import scala.util.control.Exception._

  def divide(x: Int, y: Int) = x / y

  allCatch withTry { divide(0, 0) } match {
    case Success(i) => println(i)
    case Failure(t) => t.printStackTrace()
  }
  • Exception3
/**
 * allCatch withApply andFinallyでtry catch finnaly
 * withTryよりもwithApplyの方が好きかも。withApplyの後に例外時の処理、applyの後に処理を書く。
 */
import scala.util.control.Exception._

def divide(x: Int, y: Int) = x / y

val result = allCatch withApply { t: Throwable => -1 } apply divide(0, 0)
println(result)
  //=> -1

//これにandFinallyを使うとfinallyも書ける。

import scala.util.control.Exception._

def divide(x: Int, y: Int) = x / y

val result = allCatch withApply { t: Throwable => -1 } andFinally { println("finally") } apply divide(0, 0)
  //=> finally
println(result)
  //=> -1
  • Exception4
/**
optやeitherでもthrowされる例外

optやeitherなどを使って例外を処理した場合、ControlThrowableとInterruptedExceptionはcatchされても再度throwされるらしい。breakを途中で止められても困るしね。

下記の例では途中でBreakControl(ControlThrowableを継承している)をthrowしている為、withApplyは呼び出されずにそのまま例外がthrowされる。
*/
  // scala.util.control.BreakControlを起こすだけの関数
  def break() = Breaks.break()

  val result = allCatch withApply { t: Throwable => -1 } apply break()
    //=> Exception in thread "main" scala.util.control.BreakControl
  println(result)

/**
nonFatalCatch

nonFatalCatchでは深刻でない例外のみcatchする。具体的にはVirtualMachineError, ThreadDeath, LinkageErrorなどが対象になるらしい。

下記の場合、1つ目のnonFatalCatchはException(fatalでない)なのでNoneが返るが、2つ目はLinkageErrorをthrowしているので例外がthrowされる。
*/
var result = nonFatalCatch opt { throw new Exception }
println(result)
  //=> None

result = nonFatalCatch opt { throw new LinkageError() }
  //=> Exception in thread "main" java.lang.LinkageError
println(result)

/**
ultimately

ultimatelyを使うとcatchなしでfinallyのみ記述できる。
*/
  def divide(x: Int, y: Int) = x / y

  ultimately { println("finally") } { divide(0, 0) }

/**
warningならログだけ出力してOptionを返したい

scala-loggingが入ってるものとして、LazyLoggingを継承したtrait作って、そこにoptWithWarnというメソッドを追加してみる。

 */
import scala.util.control.Exception._

trait LazyLogging extends com.typesafe.scalalogging.LazyLogging {

  def optWithWarn[U](body: => U): Option[U] =
    allCatch withApply { t: Throwable => logger.warn(t.getMessage, t); None } apply { Some(body) }
}

/**
これで例外時はwarnを出してNoneを返す処理になるはず。未テスト。
ignoringで例外を無視する

ignoringを使うと例外を無視する。但しclassofで例外を指定しないといけない。
*/
  def something() = { println("something"); throw new Exception }

  val result = ignoring(classOf[Throwable]) { something() }
    //=> something
  println(result)
    //=> ()

/**
handlingで例外毎にハンドリング

handlingでorして複数の例外の処理を書いてみる。

とりあえず単一の例外を取得。
*/
def divide(x: Int, y: Int) = { x / y }

handling(classOf[ArithmeticException]) by { e: Throwable => println("ArithmeticException") } apply { divide(0, 0) }
/**
handlingの引数には複数クラスを指定できる。handling(classOf[ArithmeticException], classOf[NumberFormatException])のように。

またorで繋ぐことで例外ごとにhandlingを変えることもできる。
*/
(handling(classOf[ArithmeticException]) by { e: Throwable => println("ArithmeticException") }) or
  (handling(classOf[NullPointerException]) by { e: Throwable => println("NullPointerException") }) or
  (handling(classOf[NumberFormatException]) by { e: Throwable => println("NumberFormatException") }) apply
  { divide(0, 0) }

/**
handlingの処理をあらかじめ記述しておくとスッキリする。

*/
def divide(x: Int, y: Int) = { x / y }
val h1 = handling(classOf[NumberFormatException]) by { e: Throwable => println("NumberFormatException") }
val h2 = handling(classOf[NullPointerException]) by { e: Throwable => println("NullPointerException") }
val h3 = handling(classOf[NumberFormatException]) by { e: Throwable => println("NumberFormatException") }

h1 or h2 or h3 apply divide(0, 0)

/**
scala.util.Try

Tryでwrapすれば、getOrElseで失敗時の値を設定したり、recoverで例外のハンドリングができる。

下記はgetOrElseで失敗時は-1を返すようにしている。
*/
import scala.util.Try

def divide(x: Int, y: Int) = Try { x / y }

val result = divide(0, 0).getOrElse(-1)
println(result)
  #=> -1

// getを用いると、例外でなければ値が取得でき、例外が発生していれば当該の例外がthrowされる。

def divide(x: Int, y: Int) = Try { x / y }

val result1 = divide(4, 2).get
println(result1)
  //=> 2

val result2 = divide(0, 0).get
println(result2)
  //=> Exception in thread "main" java.lang.ArithmeticException: / by zero

// Exceptionの方でもやったようにSuccess/Failureでパターンマッチできる。

def divide(x: Int, y: Int) = Try { x / y }

divide(0, 0) match {
  case Success(i) => println(i)
  case Failure(e) => println(e.getMessage)
}
  //=> / by zero

/**
foreachで成功時だけ処理を行わせる。

下記のケースでは前者は例外にならないので結果が標準出力され、後者は例外が発生しているので何も出力されない。
*/
def divide(x: Int, y: Int) = Try { x / y }

divide(3, 1) foreach println
  //=> 3
divide(0, 0) foreach println
  //=>

/**
recoverで例外のハンドリング

recoverはFailureの中身に対して処理をしてSuccessに置き換えることができる。

下記の例では0除算で例外が起きているが、recover内でArithmeticExceptionの場合は標準出力して-1を返すと記述されている為、結果はSuccess(-1)が返る。
*/
def divide(x: Int, y: Int) = Try { x / y }

val t = divide(3, 0) recover {
  case e: IOException           => { println("IOException"); -1 }
  case e: ArithmeticException   => { println("ArithmeticException"); -1 }
  case e: NumberFormatException => { println("NumberFormatException"); -1 }
  case e                        => throw e
}
  //=> ArithmeticException

println(t)
  //=> Success(-1)

/**
recoverWithだと勝手にSuccessでwrapされない。

下記の例では指定した例外の場合はSuccessで返し、それ以外の例外であればFailureで返している。
*/
val t = divide(3, 0) recoverWith {
  case e: ArithmeticException   => Success(-1)
  case e: NumberFormatException => Success(-1)
  case e                        => Failure(e)
}
  //=> ArithmeticException

println(t)
  //=> Success(-1)

/**
Tryは基本、NonFatal

TryはNonFatalな例外のみFailureで返し、それ以外だとそのまま例外をthrowする。

下記のようにFatalなLinkageErrorが発生した場合や、BreakなどのControlThrowable、InterruptedExceptionが発生した場合はcatchされない。
*/
def something() = Try { throw new LinkageError() }

something()
  //=> Exception in thread "main" java.lang.LinkageError
  • MongoDB
import org.mongodb.scala.bson.BsonDocument
import org.mongodb.scala.{Completed, MongoClient, MongoCollection, Observer}
import org.mongodb.scala.bson.collection.mutable.Document
import scala.concurrent.duration._
import scala.concurrent.{Await, Future}


case class MongoDBSink(db: String, collection: String, host: String)
    extends DataSink {

  import MongoDBSink._

  override def write(data: Array[String]): Unit = {
    val collection = getCollection(this)

    data
      .grouped(1000)
      .foreach(docs => {
        val observable =
          collection.insertMany(docs.map(str => Document(BsonDocument(str))))
        Await.result(observable.head(), 10 seconds)
      })
    logger.info(s"Total docs inserted : ${data.length}")
  }

}

object MongoDBSink {

  import org.json4s._
  import org.json4s.jackson.JsonMethods._

  def apply(uri: String): MongoDBSink = {
    val splitUri = uri.substring(10).split("/")
    val hostname = "mongodb://" + splitUri(0)
    val database = splitUri(1)
    val collection = splitUri(2).split("\\?")(0)
    MongoDBSink(database, collection, hostname)
  }

  def getCollection(mongo: MongoDBSink): MongoCollection[Document] = {
    val mongoClient = MongoClient(mongo.host)
    mongoClient.getDatabase(mongo.db).getCollection(mongo.collection)
  }

  def jsonStrToMap(jsonStr: String): Map[String, Any] = {
    implicit val formats = org.json4s.DefaultFormats
    parse(jsonStr).extract[Map[String, Any]]
  }
} 

Reference

Recommend