Scala + 柯里化函数的无形抽象
Scala + Shapeless abstract over curried function
我正在尝试弄清楚如何对柯里化函数进行抽象。
我可以通过以下方式抽象出一个未柯里化的函数:
def liftAU[F, P <: Product, L <: HList, R, A[_]](f: F)
(implicit
fp: FnToProduct.Aux[F, L => R],
gen: Generic.Aux[P, L],
ap: Applicative[A]
): A[P] => A[R] = p => p.map(gen.to).map(f.toProduct)
这将采用 (Int, Int) => Int 之类的函数并将其转换为 Option[(Int, Int)] => Option[Int] 之类的东西。它适用于任何功能。
我想创建一个柯里化版本,它将采用像 Int => Int => Int 这样的函数并将其转换为 Option[Int] => Option[Int] => Option[Int]。
它也应该适用于任何数量的柯里化函数。
由于 FnToProduct 仅适用于第一个参数列表,它在这里没有帮助,我也尝试在类型级别编写一些递归定义,但我在定义类型时遇到问题。
不确定是否可行,但很想知道其他人是否尝试过类似的方法。
你可以定义递归Poly
object constNone extends Poly1 {
implicit def zeroCase[In]: Case.Aux[In, Option[Int]] = at(_ => None)
implicit def succCase[In, In1, Out](implicit
cse: Case.Aux[In, Out]): Case.Aux[In1, In => Out] = at(_ => cse(_))
}
object transform extends Poly1 {
implicit def zeroCase: Case.Aux[Int, Option[Int]] = at(Some(_))
implicit def succCase[In, Out](implicit
cse: Case.Aux[In, Out],
noneCase: constNone.Case.Aux[In, Out]
): Case.Aux[Int => In, Option[Int] => Out] =
at(f => {
case Some(n) => cse(f(n))
case None => noneCase(f(0))
})
}
(transform((x: Int) => (y: Int) => x + y) _)(Some(1))(Some(2)) //Some(3)
(transform((x: Int) => (y: Int) => x + y) _)(Some(1))(None) //None
(transform((x: Int) => (y: Int) => x + y) _)(None)(Some(2)) //None
Dmytro 的答案实际上对我不起作用,除非我更改其中一个对象中的实例名称,即使那样它也不适用于像 Int => Int => Int => Int
这样的函数,我发现使用 Poly
值真的很烦人,所以我不会调试以前的答案,而是要写我自己的。
您实际上可以使用 100% 无形状的类型很好地编写此操作 class:
import cats.Applicative
trait LiftCurried[F[_], I, O] {
type Out
def apply(f: F[I => O]): F[I] => Out
}
object LiftCurried extends LowPriorityLiftCurried {
implicit def liftCurried1[F[_]: Applicative, I, I2, O2](implicit
lc: LiftCurried[F, I2, O2]
): Aux[F, I, I2 => O2, F[I2] => lc.Out] = new LiftCurried[F, I, I2 => O2] {
type Out = F[I2] => lc.Out
def apply(f: F[I => I2 => O2]): F[I] => F[I2] => lc.Out =
(Applicative[F].ap(f) _).andThen(lc(_))
}
}
trait LowPriorityLiftCurried {
type Aux[F[_], I, O, Out0] = LiftCurried[F, I, O] { type Out = Out0 }
implicit def liftCurried0[F[_]: Applicative, I, O]: Aux[F, I, O, F[O]] =
new LiftCurried[F, I, O] {
type Out = F[O]
def apply(f: F[I => O]): F[I] => F[O] = Applicative[F].ap(f) _
}
}
可能可以使它更简洁一些,但我发现它现在的可读性是合理的。
您可能想要像这样的具体内容:
def liftCurriedIntoOption[I, O](f: I => O)(implicit
lc: LiftCurried[Option, I, O]
): Option[I] => lc.Out = lc(Some(f))
然后我们可以证明它可以像这样使用一些函数:
val f: Int => Int => Int = x => y => x + y
val g: Int => Int => Int => Int = x => y => z => x + y * z
val h: Int => Int => Int => String => String = x => y => z => _ * (x + y * z)
然后:
scala> import cats.instances.option._
import cats.instances.option._
scala> val ff = liftCurriedIntoOption(f)
ff: Option[Int] => (Option[Int] => Option[Int]) = scala.Function1$$Lambda44/350671260@73d06630
scala> val gg = liftCurriedIntoOption(g)
gg: Option[Int] => (Option[Int] => (Option[Int] => Option[Int])) = scala.Function1$$Lambda44/350671260@2bb9b82c
scala> val hh = liftCurriedIntoOption(h)
hh: Option[Int] => (Option[Int] => (Option[Int] => (Option[String] => Option[String]))) = scala.Function1$$Lambda44/350671260@45eec9c6
我们还可以再应用几次,只是为了它的地狱:
scala> val hhhh = liftCurriedIntoOption(liftCurriedIntoOption(hh))
hhh: Option[Option[Option[Int]]] => (Option[Option[Option[Int]]] => (Option[Option[Option[Int]]] => (Option[Option[Option[String]]] => Option[Option[Option[String]]]))) = scala.Function1$$Lambda44/350671260@592593bd
所以类型看起来没问题,对于值…
scala> ff(Some(1))(Some(2))
res0: Option[Int] = Some(3)
scala> ff(Some(1))(None)
res1: Option[Int] = None
scala> hh(Some(1))(None)(None)(None)
res2: Option[String] = None
scala> hh(Some(1))(Some(2))(Some(3))(Some("a"))
res3: Option[String] = Some(aaaaaaa)
...我认为这就是您的目标。
我正在尝试弄清楚如何对柯里化函数进行抽象。 我可以通过以下方式抽象出一个未柯里化的函数:
def liftAU[F, P <: Product, L <: HList, R, A[_]](f: F)
(implicit
fp: FnToProduct.Aux[F, L => R],
gen: Generic.Aux[P, L],
ap: Applicative[A]
): A[P] => A[R] = p => p.map(gen.to).map(f.toProduct)
这将采用 (Int, Int) => Int 之类的函数并将其转换为 Option[(Int, Int)] => Option[Int] 之类的东西。它适用于任何功能。
我想创建一个柯里化版本,它将采用像 Int => Int => Int 这样的函数并将其转换为 Option[Int] => Option[Int] => Option[Int]。
它也应该适用于任何数量的柯里化函数。
由于 FnToProduct 仅适用于第一个参数列表,它在这里没有帮助,我也尝试在类型级别编写一些递归定义,但我在定义类型时遇到问题。
不确定是否可行,但很想知道其他人是否尝试过类似的方法。
你可以定义递归Poly
object constNone extends Poly1 {
implicit def zeroCase[In]: Case.Aux[In, Option[Int]] = at(_ => None)
implicit def succCase[In, In1, Out](implicit
cse: Case.Aux[In, Out]): Case.Aux[In1, In => Out] = at(_ => cse(_))
}
object transform extends Poly1 {
implicit def zeroCase: Case.Aux[Int, Option[Int]] = at(Some(_))
implicit def succCase[In, Out](implicit
cse: Case.Aux[In, Out],
noneCase: constNone.Case.Aux[In, Out]
): Case.Aux[Int => In, Option[Int] => Out] =
at(f => {
case Some(n) => cse(f(n))
case None => noneCase(f(0))
})
}
(transform((x: Int) => (y: Int) => x + y) _)(Some(1))(Some(2)) //Some(3)
(transform((x: Int) => (y: Int) => x + y) _)(Some(1))(None) //None
(transform((x: Int) => (y: Int) => x + y) _)(None)(Some(2)) //None
Dmytro 的答案实际上对我不起作用,除非我更改其中一个对象中的实例名称,即使那样它也不适用于像 Int => Int => Int => Int
这样的函数,我发现使用 Poly
值真的很烦人,所以我不会调试以前的答案,而是要写我自己的。
您实际上可以使用 100% 无形状的类型很好地编写此操作 class:
import cats.Applicative
trait LiftCurried[F[_], I, O] {
type Out
def apply(f: F[I => O]): F[I] => Out
}
object LiftCurried extends LowPriorityLiftCurried {
implicit def liftCurried1[F[_]: Applicative, I, I2, O2](implicit
lc: LiftCurried[F, I2, O2]
): Aux[F, I, I2 => O2, F[I2] => lc.Out] = new LiftCurried[F, I, I2 => O2] {
type Out = F[I2] => lc.Out
def apply(f: F[I => I2 => O2]): F[I] => F[I2] => lc.Out =
(Applicative[F].ap(f) _).andThen(lc(_))
}
}
trait LowPriorityLiftCurried {
type Aux[F[_], I, O, Out0] = LiftCurried[F, I, O] { type Out = Out0 }
implicit def liftCurried0[F[_]: Applicative, I, O]: Aux[F, I, O, F[O]] =
new LiftCurried[F, I, O] {
type Out = F[O]
def apply(f: F[I => O]): F[I] => F[O] = Applicative[F].ap(f) _
}
}
可能可以使它更简洁一些,但我发现它现在的可读性是合理的。
您可能想要像这样的具体内容:
def liftCurriedIntoOption[I, O](f: I => O)(implicit
lc: LiftCurried[Option, I, O]
): Option[I] => lc.Out = lc(Some(f))
然后我们可以证明它可以像这样使用一些函数:
val f: Int => Int => Int = x => y => x + y
val g: Int => Int => Int => Int = x => y => z => x + y * z
val h: Int => Int => Int => String => String = x => y => z => _ * (x + y * z)
然后:
scala> import cats.instances.option._
import cats.instances.option._
scala> val ff = liftCurriedIntoOption(f)
ff: Option[Int] => (Option[Int] => Option[Int]) = scala.Function1$$Lambda44/350671260@73d06630
scala> val gg = liftCurriedIntoOption(g)
gg: Option[Int] => (Option[Int] => (Option[Int] => Option[Int])) = scala.Function1$$Lambda44/350671260@2bb9b82c
scala> val hh = liftCurriedIntoOption(h)
hh: Option[Int] => (Option[Int] => (Option[Int] => (Option[String] => Option[String]))) = scala.Function1$$Lambda44/350671260@45eec9c6
我们还可以再应用几次,只是为了它的地狱:
scala> val hhhh = liftCurriedIntoOption(liftCurriedIntoOption(hh))
hhh: Option[Option[Option[Int]]] => (Option[Option[Option[Int]]] => (Option[Option[Option[Int]]] => (Option[Option[Option[String]]] => Option[Option[Option[String]]]))) = scala.Function1$$Lambda44/350671260@592593bd
所以类型看起来没问题,对于值…
scala> ff(Some(1))(Some(2))
res0: Option[Int] = Some(3)
scala> ff(Some(1))(None)
res1: Option[Int] = None
scala> hh(Some(1))(None)(None)(None)
res2: Option[String] = None
scala> hh(Some(1))(Some(2))(Some(3))(Some("a"))
res3: Option[String] = Some(aaaaaaa)
...我认为这就是您的目标。