【Scala】Scala ~ implicit ~

■ はじめに

https://dk521123.hatenablog.com/entry/2024/07/23/193246

で、sealed trait をやったが、
それを拡張するためのメソッドを追加したく、
イメージとしては、C#の拡張メソッドをScalaでやりたい。

Scala の implicit がやりたいことができそうなので、
メモしておく。

目次

【1】Implicit conversion
【2】implicit class
【3】import scala.language.implicitConversions
【4】サンプル
 例1:Hello world
 例2:Implicit parameter(文脈引き渡し)
 例3:implicit class

【1】Implicit conversion

* 暗黙の型変換をユーザが定義できる機能
 => 便利だが、使いすぎるとコードが追いづらくなる、、、

cf. implicit (インプリジット) = 暗黙の <-> explicit 明確な、明白な
* 以下のサイトが分かりやすい

http://techtipshoge.blogspot.com/2019/06/scala-implicit.html
https://zenn.dev/at12/articles/7ac49ccbe4865b
https://scala-text.github.io/scala_text/implicit.html

【2】implicit class

* C#の拡張メソッドみたいに実装できる
 => 実装例は、「例3:implicit class」を参照

C#】拡張メソッド
https://dk521123.hatenablog.com/entry/2011/02/22/224208

参考文献
https://blog.foresta.me/posts/scala-extension-method/
https://qiita.com/ka2kama/items/e696c0f0460cd5cefccb
https://zenn.dev/ababup1192/articles/29c7b3d4bf74bd

【3】import scala.language.implicitConversions

* implicit を使用した際の警告表示を抑制してくれる

https://docs.scala-lang.org/ja/tour/implicit-conversions.html

より抜粋
~~~~~~~~
暗黙の変換は見境なく使われると落とし穴になり得るため、
暗黙の変換の定義をコンパイルしている時にコンパイラは警告を出します。

警告をオフにするには、次のいずれかの措置を講じてください。

・暗黙の変換定義のスコープにscala.language.implicitConversionsをインポートする。
・コンパイラを-language:implicitConversionsをつけて起動する
・コンパイラにより変換が適用された時、警告は出ません。
~~~~~~~~

【4】サンプル

例1:Hello world

object Main extends App {
  class Hello(val name: String) {
    def printHello(): Unit = {
      println(s"Hello, ${this.name}!!!")
    }
  }

  class World(val name: String) {
    def printWorld(): Unit = {
      println(s"World, ${this.name}...")
    }
  }

  // ★implicit で定義★
  implicit def toWorld(hello: Hello): World = {
    new World(hello.name)
  }

  val hello = new Hello("Mike")
  hello.printHello // Hello, Mike!!!
  hello.printWorld // World, Mike... <= 勝手にtoWorldを呼び出してくれる
}

例2:Implicit parameter(文脈引き渡し)

object Main extends App {

  def sayHi(greeting: String)(implicit name: String): Unit = {
    println(s"${greeting}, ${name}!!!")
  }

  // 暗黙のパラメータを事前に設定
  implicit val name = "Mike"

  sayHi("Hello") // Hello, Mike!!!
}

例3:implicit class

import ConfigType._

import scala.language.implicitConversions

sealed trait ConfigType
object ConfigType {
  final case class ConfigString(str: String) extends ConfigType
  final case class ConfigInt(int: Int) extends ConfigType
  final case class ConfigBoolean(bool: Boolean) extends ConfigType
  final case class ConfigStrings(list: List[String]) extends ConfigType

  def toConfigType[T](value: T): ConfigType = {
    value match {
      case intValue: Int =>  ConfigInt(intValue)
      case boolValue: Boolean => ConfigBoolean(boolValue)
      case stringValue: String => ConfigString(stringValue)
      case listValues: List[String] => ConfigStrings(listValues)
      case _ => throw new IllegalArgumentException("No supported")
    }
  }
  // ★implicit classを使う★
  implicit class ConfigTypeEx(val self: ConfigType) {
    // ★C#の拡張メソッドっぽく呼び出せる
    final def toPrintStr: String = {
      self match {
        case intValue: ConfigInt => s"Int - ${intValue.int}"
        case boolValue: ConfigBoolean => s"Boolean - ${boolValue.bool}"
        case stringValue: ConfigString => s"String - ${stringValue.str}"
        case listValues: ConfigStrings => s"List - ${listValues.list.mkString(",")}"
        case _ => "No supported"
      }
    }
  }
}



object Hello {
  def main(args: Array[String]): Unit = {
    val mapper: Map[String, ConfigType] = Map(
      "key1" -> toConfigType("Hello"),
      "key2" -> toConfigType(2),
      "key3" -> toConfigType(true),
      "key4" -> toConfigType(List("value4_1", "value4_2", "value4_3"))
    )
    mapper.foreach { case (key: String, value: ConfigType) =>
      // ★value.toPrintStrを注目★
      println(s"${key} - ${value.toPrintStr}}")
    }
  }
}

関連記事

Scala ~ 環境構築編 ~
https://dk521123.hatenablog.com/entry/2023/03/10/193805
Scala ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2023/03/12/184331
Scala ~ 基本編 / コレクション ~
https://dk521123.hatenablog.com/entry/2023/03/13/000345
Scala ~ コレクションで使えるメソッド ~
https://dk521123.hatenablog.com/entry/2023/09/07/223422
Scala ~ 基本編 / メソッド ~
https://dk521123.hatenablog.com/entry/2023/03/03/000000
Scala ~ 基本編 / 繰り返し ~
https://dk521123.hatenablog.com/entry/2023/01/24/000000
Scala ~ 基本編 / トレイト ~
https://dk521123.hatenablog.com/entry/2023/09/10/204016
Scala ~ 基本編 / パターンマッチング ~
https://dk521123.hatenablog.com/entry/2023/06/06/233614
ScalaEnum
https://dk521123.hatenablog.com/entry/2023/01/05/000000
Scala ~ asInstanceOf / isInstanceOf ~
https://dk521123.hatenablog.com/entry/2024/07/26/225713
C#】拡張メソッド
https://dk521123.hatenablog.com/entry/2011/02/22/224208