Scala でリフティング
この記事では、Scala プログラミング言語でのリフティングについて説明します。リフティングにはさまざまな意味があります。それは私たちがそれを使用する文脈に依存します。
それらを一つずつ見ていきましょう。
メソッドを関数に変換する
メソッドを関数に変換したいという状況に遭遇することがあります。これはリフティングの一例です。
たとえば、次のメソッドがあるとします。
def mul(x: Int) = x*5
def isEven(x: Int) = x%2==0
これで、これらのメソッドを次のように構成できます。
isEven(mul(4))
しかし、この構成を機能的な方法で実行したい場合はどうでしょうか。次に、これらの上記のメソッドを関数に変換する必要があります。
これは、Scala の _
記号を使用して持ち上げることで実行できます。
val mulAns = mul _
val isEvenAns = isEven _
これで、次のように機能的に構成できます。
( mulAns andThen isEvenAns)(5)
完全に機能するコード:
object MyClass {
def mul(x: Int) = x*5
def isEven(x: Int) = x%2==0
def main(args: Array[String])
{
println(isEven(mul(4)));
val mulAns = mul _
val isEvenAns = isEven _
println(( mulAns andThen isEvenAns)(5))
}
}
出力:
true
false
説明:
最初の出力は true
です。4 に 5 を掛けると、20 になります。この 20 は、パラメーターとして isEven
関数に渡されます。
2 番目の出力は false
です。最初に 5 に 5 を掛けてから、その結果を isEven
関数でチェックします。
Scala で純粋な関数を Functor に変換する
Functor は、エンティティまたはオブジェクトの関数のように、カテゴリ間のマッピングを指します。たとえば、カテゴリ A
と B
と、A's
オブジェクトを B's
オブジェクトにマップするファンクタ F
があります。
trait Functor[F[_]] {
def mapping[A, B](X: F[A])(f: A => B): F[B]
def lifting[A, B](f: A => B): F[A] => F[B] =
X => map(X)(f)
}
この文脈では、リフティングとは、A=>B
から関数を取得し、それを F[A] => F[B]
の形式の関数にすることによって関数に変換することを指します。ネストされたデータタイプを操作する場合、これは非常に便利です。
Scala で部分関数を関数に変換する
このコンテキストでは、リフティングはドメインの拡張を指します。部分関数は、値のサブドメインに適用できる関数の一種です。
しかし、時にはその領域を拡張したくなることがある。それを実現するのがリフティングである。
正の数の平方根を与える部分関数があるとしましょう。
val sqrt: PartialFunction[Double, Double] =
{
case temp if temp >= 0 => Math.sqrt(temp)
}
これで、lift
メソッドを使用して部分関数のドメインを拡張し、よりエレガントに見せることができます。
def getSqrtRootPartialFunction(t: Double) =
{
sqrt.lift(t).map(ans => s"Square root of ${t} is ${ans}")
.getOrElse(s"Cannot calculate square root for t")
}
完全に機能するコード:
object MyClass {
val sqrt: PartialFunction[Double, Double] = {
case temp if temp >= 0 => Math.sqrt(temp)
}
def getSqrtRootPartialFunction(t: Double) = {
sqrt.lift(t).map(ans => println(s"Square root of ${t} is ${ans}"))
.getOrElse(println(s"Cannot calculate square root for t"))
}
def main(args: Array[String])
{
getSqrtRootPartialFunction(35)
}
}
出力:
Square root of 35.0 is 5.916079783099616
説明:
したがって、ここで sqrt
関数を解除すると、ドメインが PartialFunction[Double, Double]
から Function[Double, Option[Double]]
に完全に拡張されます。
時々、部分関数を持ち上げることは、範囲外のインデックス
例外を回避するために使用されます。
Seq("1", "2", "3").lift(1) // Some("2")
Seq("1", "2", "3").lift(7) // none returned
ドメインが String
から Option[String]
に拡張されました。したがって、範囲外の値にアクセスしようとすると、解除された Seq
は、範囲外
の例外をスローする代わりに、None
を返します。
Scala のモナドトランスフォーマー
モナドを組み合わせるには、モナドトランスフォーマーを使用します。2つの Futures
があるとしましょう。
val helloWorld: Future[Option[String]] = Future.successful(Some("hello world"))
val language: Future[String] = Future.successful("this is Scala language")
現在、両方の Futures
は異なるドメインを持っているため、language
を OptionT
に持ち上げることでモナドトランスフォーマーを使用できます。これにより、より一貫した方法で結果を処理できます。
def MonadTransformer() = {
val message: OptionT[Future, String] = for {
hello <- OptionT(helloWorld)
name <- OptionT.liftF(language)
} yield println(s"$hello $name")
val result: Future[Option[String]] = message.value
Await.result(result, 1 second)
}
まとめ
この記事では、リフティングの概念と、さまざまなコンテキストでのリフティングの意味を学びました。コードをより構成可能で慣用的な方法で作成すると、コードのさまざまな部分を構築するだけで時間を無駄にするのではなく、コードのビジネスロジックに集中できるようになるため、非常に便利です。