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]]
}
}