Skip to content

Instantly share code, notes, and snippets.

@j5ik2o
Last active May 18, 2017 15:14
Show Gist options
  • Save j5ik2o/963781232ee55f1b6f7024722ff83041 to your computer and use it in GitHub Desktop.
Save j5ik2o/963781232ee55f1b6f7024722ff83041 to your computer and use it in GitHub Desktop.
package example
/**
* 値が存在する・しないの両状態を表す型を表すトレイト。
*
* @tparam A 値の型
*/
sealed trait MyOption[+A] {
/**
* 格納された値を返す。
*
* @return 値
* @throws NoSuchElementException 値が存在しない場合
*/
def get: A
/**
* 値がないかどうかを返す。
*
* @return 値が存在しない場合はtrue。
*/
def isEmpty: Boolean
/**
* 値が存在する場合に、値の変換を行う。
*
* @param f 値を変換するための関数
* @tparam B 新しい型
* @return 新しい [[MyOption]]
*/
def map[B](f: A => B): MyOption[B] = this match {
case MyNone => MyNone
case MySome(v) => MySome(f(v))
case MyWithFilter(op, p) =>
if (op.isEmpty)
MyNone
else {
val x = op.get
if (p(x))
MySome(f(x))
else
MyNone
}
}
/**
* 値が存在する場合に、値の変換を行う。
*
* @param f 値を変数するための関数
* @tparam B 新しい型
* @return 新しい [[MyOption]]
*/
def flatMap[B](f: A => MyOption[B]): MyOption[B] = map(f).getOrElse(MyNone)
/**
* 値が存在する場合に、値をフィルタリングする。
*
* @param f フィルターのための述語関数
* @return 新しい [[MyOption]]
*/
def filter(f: A => Boolean): MyOption[A] = this match {
case MySome(v) if f(v) => this
case MyWithFilter(op, p) =>
if (op.isEmpty)
MyNone
else {
val x = op.get
if (p(x) && f(x))
op
else
MyNone
}
case _ => MyNone
}
/**
* [[filter]]の遅延評価版。難易度高
*
* 関数fはすぐには評価できません。`withFiler`以外のメソッドを利用した際に、評価してください。
* 難しい場合はfilterメソッドと同じ実装でも構いません。
*
* @param f フィルターのための述語関数
* @return 新しい [[MyOption]]
*/
def withFilter(f: A => Boolean): MyOption[A] = MyWithFilter(this, f)
/**
* 格納された値を返す。値がない場合は指定された値を返す。
*
* @param elseValue 値がない場合に返す値
* @tparam B 新しい要素型
* @return 値
*/
def getOrElse[B >: A](elseValue: B): B = this match {
case MyNone => elseValue
case MySome(v) => v
case MyWithFilter(op, p) =>
if (op.isEmpty)
elseValue
else {
val x = op.get
if (p(x))
x
else
elseValue
}
}
/**
* 値が存在しない場合に、指定した式を評価し返す。
*
* @param elseValue 値が存在しない場合に返す式
* @tparam B 新しい要素型
* @return elseValueを評価した値
*/
def orElse[B >: A](elseValue: => MyOption[B]): MyOption[B] = this match {
case MyNone => elseValue
case MySome(_) => this
case MyWithFilter(op, p) =>
if (op.isEmpty)
elseValue
else {
val x = op.get
if (p(x))
op
else
elseValue
}
}
}
private case class MyWithFilter[A](op: MyOption[A], p: A => Boolean) extends MyOption[A] {
override def get: A = op.filter(p).get
override def isEmpty: Boolean = op.filter(p).isEmpty
}
/**
* 値が存在ない場合の[[MyOption]]。
*/
case object MyNone extends MyOption[Nothing] {
override def get: Nothing = throw new NoSuchElementException
override def isEmpty: Boolean = true
}
/**
* 値が存在する場合の[[MyOption]]。
*
* @param value 値
* @tparam A 値の型
*/
case class MySome[+A](value: A) extends MyOption[A] {
override def get: A = value
override def isEmpty: Boolean = false
}
/**
* [[MyOption]]のコンパニオンオブジェクト。
*/
object MyOption {
/**
* ファクトリメソッド。
*
* `value`が`null`以外であれば`MySome`にラップして返す。
* それ以外は`MyNone`を返す。
*
* @param value 値
* @tparam A 値の型
* @return [[MyOption]]
*/
def apply[A](value: A): MyOption[A] = if (value == null) MySome(value) else MyNone
}
package example
object MyOptionMain extends App {
def f(x: Int): MyOption[Int] = MySome(x * 2)
def g(x: Int): MyOption[Int] = MySome(x + 3)
assert(MyNone.isEmpty)
assert(!MySome(100).isEmpty)
assert(MySome(100).get == 100)
try {
MyNone.get
assert(false)
} catch {
case _: NoSuchElementException =>
}
assert(MySome(100).getOrElse(20) == 100)
assert((MyNone: MyOption[Int]).getOrElse(20) == 20)
assert(MySome(100).orElse(MySome(20)) == MySome(100))
assert(MyNone.orElse(MySome(20)) == MySome(20))
assert(MySome(100).filter(_ > 0) == MySome(100))
assert(MySome(100).filter(_ < 0) == MyNone)
assert(MySome(100).map(_ * 2) == MySome(200))
assert((MyNone: MyOption[Int]).map(_ * 2) == MyNone)
assert(MySome(100).flatMap(f) == f(100))
assert(MySome(100).flatMap(MySome(_)) == MySome(100))
assert((MyNone: MyOption[Int]).flatMap(MySome(_)) == MyNone)
assert(MySome(100).flatMap(f).flatMap(g) == MySome(100).flatMap(x => f(x).flatMap(g)))
assert((MyNone: MyOption[Int]).flatMap(f).flatMap(g) == (MyNone: MyOption[Int]).flatMap(x =>
f(x).flatMap(g)))
// --- withFilterのテスト
assert(MySome(100).withFilter(_ > 0).getOrElse(20) == 100)
assert(MySome(100).withFilter(_ < 0).getOrElse(20) == 20)
assert((MyNone: MyOption[Int]).withFilter(_ > 0).getOrElse(20) == 20)
assert((MyNone: MyOption[Int]).withFilter(_ < 0).getOrElse(20) == 20)
assert(MySome(100).withFilter(_ > 0).orElse(MySome(20)) == MySome(100))
assert(MySome(100).withFilter(_ < 0).orElse(MySome(20)) == MySome(20))
assert((MyNone: MyOption[Int]).withFilter(_ > 0).orElse(MySome(20)) == MySome(20))
assert((MyNone: MyOption[Int]).withFilter(_ < 0).orElse(MySome(20)) == MySome(20))
assert(MySome(100).withFilter(_ > 0).filter(_ > 0) == MySome(100))
assert(MySome(100).withFilter(_ < 0).filter(_ > 0) == MyNone)
assert(MySome(100).withFilter(_ > 0).filter(_ < 0) == MyNone)
assert(MySome(100).withFilter(_ < 0).filter(_ < 0) == MyNone)
assert(MySome(100).withFilter(_ > 0).map(_ * 2) == MySome(200))
assert(MySome(100).withFilter(_ < 0).map(_ * 2) == MyNone)
assert((MyNone: MyOption[Int]).withFilter(_ > 0).map(_ * 2) == MyNone)
assert((MyNone: MyOption[Int]).withFilter(_ < 0).map(_ * 2) == MyNone)
assert(MySome(100).withFilter(_ > 0).flatMap(f) == f(100))
assert(MySome(100).withFilter(_ > 0).flatMap(MySome(_)) == MySome(100))
assert((MyNone: MyOption[Int]).withFilter(_ > 0).flatMap(MySome(_)) == MyNone)
assert(
MySome(100).withFilter(_ > 0).flatMap(f).flatMap(g) == MySome(100)
.withFilter(_ > 0)
.flatMap(x => f(x).flatMap(g)))
assert(
(MyNone: MyOption[Int]).withFilter(_ > 0).flatMap(f).flatMap(g) == (MyNone: MyOption[Int])
.withFilter(_ > 0)
.flatMap(x => f(x).flatMap(g)))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment