Scalaにおいて、一部の型引数だけを明示して部分適用し、残りの型引数は推論させたい、というパターンが稀に存在します。
そういう場面に遭遇しない人は別に今あまり読む必要はないです。
typelevel/catsのドキュメントでは Partially-Applied Type
と呼ばれています。
(余談 https://x.com/xuwei_k/status/1547054067880697867 )
https://typelevel.org/cats/guidelines.html#partially-applied-type
そして、2022年の時点でScala 3のPolymorphic Function Typeを使って短く定義出来る方法が以下で紹介されています。
さて、Scala 3.6から Clause Interleavingという、型引数の定義を分割して途中に記述することが可能になりました。
(正確にはもう少し前から実験的機能としては入っているが、正式な機能になったのが3.6から)
https://docs.scala-lang.org/sips/clause-interleaving.html
上記のPolymorphic Function Typeを使う方法では、実用上問題にならないかもしれませんが、原理上は、無駄にPolymorphic Functionが生成されて気持ち悪い問題がありました。
それが一応このClause Interleavingを使うと解決するはずです。
つまり
Scala 2
final class PurePartiallyApplied[F[_]](val dummy: Boolean = true ) extends AnyVal { def apply[A](value: A)(implicit F: Applicative[F]): OptionT[F, A] = OptionT(F.pure(Some(value))) } def pure[F[_]]: PurePartiallyApplied[F] = new PurePartiallyApplied[F]
Scala 3のPolymorphic Function
def pure[F[_]]: Applicative[F] ?=> [A] => A => OptionT[F, A] =
[A] => (a: A) => OptionT(a.some.pure)
Scala 3のClause Interleaving
def pure[F[_]](using Applicative[F])[A](a: A): OptionT[F, A] =
OptionT(a.some.pure)
javapすると以下のように普通の?メソッドになる
public static <F, A> cats.data.OptionT<F, A> pure(cats.Applicative<F>, A);
実際の例
build.sbt
libraryDependencies += "org.typelevel" %% "cats-core" % "2.12.0" scalaVersion := "3.6.1"
REPL
Welcome to Scala 3.6.1 (21.0.5, Java OpenJDK 64-Bit Server VM). Type in expressions for evaluation. Or try :help. scala> import cats._ ; import cats.syntax.all._ ; import cats.data.OptionT scala> def pure[F[_]]: Applicative[F] ?=> [A] => A => OptionT[F, A] = | [A] => (a: A) => OptionT(a.some.pure) | def pure [F[_$1]]: (cats.Applicative[F]) ?=> [A] => (x$1: A) => cats.data.OptionT[F, A] scala> pure[List](2) val res0: cats.data.OptionT[List, Int] = OptionT(List(Some(2))) scala> pure[Option]("x") val res1: cats.data.OptionT[Option, String] = OptionT(Some(Some(x)))
また、これは偶然 (using Applicative[F])
が存在したのでいい感じになっていますが、以下のような定義は怒られます
Welcome to Scala 3.6.1 (21.0.5, Java OpenJDK 64-Bit Server VM). Type in expressions for evaluation. Or try :help. scala> def foo[A][B](b: B) = ??? // Aだけ明示し、Bは推論させたい -- Error: ---------------------------------------------------------------------- 1 |def foo[A][B](b: B) = ??? // Aだけ明示し、Bは推論させたい | ^ | Type parameter lists must be separated by a term or using parameter list
これは、だいぶダサい?ですが、DummyImplicitを挟むことによって回避可能です(もっと良い案ないの・・・???)
def foo[A](using DummyImplicit)[B](b: B) = ???
いかがでしたか?
ところで、このblog書いてる2024年11月初め時点で確かに3.6.1はリリースされていますが、3.6はリリースをミスして、まだすぐ使うには微妙なので、少なくとも3.6.2が出るまで本格的なものに使うのは控えた方がいいです