是否可以描述以下函数:(any-arity => 1-arity)?

时间:2013-07-25 19:18:08

标签: scala currying

我希望能够将一个函数传递给一个类,使得函数总是返回一个布尔值,但参数的数量可以是变量。

这实际上就是我现在正在使用的:

/**
  * Template class for any SecureSocial SecuredAction.
  */
class SomeAction[T](
    isSuccessful: (SecuredRequest[AnyContent], T) => Boolean, 
    success: Result, 
    failure: Result) extends SecureSocial {

      def toReturn = SecuredAction(WithProvider("google")) {
        implicit request => if (isSuccessful(request)) success else failure
      }

}

我希望isSuccessful参数是一个函数,它至少需要一个类型SecuredRequest[AnyContent]的参数和一个零或多个参数。有时我会传入一个只需要request对象的函数,有时候我会传入一个需要request对象和其他参数的函数。

seeing this answer之后,我看着cur {,the practice of transforming a multiple-argument function into a single-argument function that will return a function if any more arguments are needed。这听起来像是可以解决我的问题,但在阅读了difference between partially applied functions and currying后,我开始想不到了?看起来仍有一定数量的最终参数...

我认为成功的讨论会如此:

class SomeAction[T](
    isSuccessful : ((SecuredRequest[AnyContent])(T) => Boolean) ,  // ERROR
    ... ) ... { ... }

这绝对不是正确的语法。

class SomeAction[T](
    isSuccessful : (SecuredRequest[AnyContent] => Boolean) , 
    ... ) ... { ... }

isSuccessful中对toReturn执行某些操作以取消传递的函数。

可能导致我得到答案的多个问题:

  • 是否有一些特定的语法来描述我不知道的curry函数?
  • 或者我有一个(SecuredRequest[AnyContent] => Boolean)类型的功能,但在isSuccessful(request)来电时是否有某种不确定性?

感谢阅读;我想我只是在寻找无关紧要的例子。

3 个答案:

答案 0 :(得分:1)

如果您的方法f类型为(A*)B,那么eta-expanded f _只是Seq[A] => B

f内部,重复的参数无论如何都有Seq[A]类型,所以它是有道理的。

有一张着名的票:

https://issues.scala-lang.org/browse/SI-4176

它以立即弃用的选项-Yeta-expand-keeps-star而闻名。

以下是一个方法的示例,该方法采用箭头左侧带星形的函数并调用它。

因此,如果“any-arity”表示“任意数量的T s”,例如类型T = (String, String),那么varargs就足够了。

object Test extends App {

  def f(g: (Int*) => Int) = g(1,2,3)

  def sum(is: Int*) = is.sum

  Console println f(sum _)

  def f2(g: (Int*) => Int, h: =>Seq[Int]): Int = g(h: _*)

  Console println f2(sum _, List(1,2,3))

  def f3(g: (Int*) => Int, is: Int*): Int = g(is: _*)

  Console println f3(sum _, 1,2,3)
}

答案 1 :(得分:1)

如果函数采用可变数量的参数,则可以执行此操作。这是必要的,因为他们事先并不知道动作会给他们多少个参数。正如其他人所提到的,可变参数函数被转换为函数,其最后一个参数是一个序列。所以你可以这样做:

case class Action[T](f: (String, Seq[T]) => Boolean);

def testfn(s: String, args: Int*): Boolean = true;

new Action[Int](testfn _);

如果你需要你的函数来表明它们的参数数量错误,你可以让它们返回Option并为每个arities添加一些辅助函数,比如

case class Action[T](f: (String, Seq[T]) => Option[Boolean]);

然后,您可以为不同的arities提供帮助,将“正常”函数转换为可变参数,如果给出错误数量的参数,则返回None

// for 1 argument functions:
def toVar[T](f: (String, T) => Boolean)(s: String, xs: Seq[T]): Option[Boolean] =
  Some(xs).collect({ case Seq(x1) => f(s, x1) });
// for 2 argument functions:
def toVar[T](f: (String, T, T) => Boolean)(s: String, xs: Seq[T]): Option[Boolean] =
  Some(xs).collect({ case Seq(x1, x2) => f(s, x1, x2) });

def testfn(s: String, x1: Int, x2: Int): Boolean =
  (x1 == x2);

new Action[Int](toVar(testfn _));

如果为toVar创建了专门用于调用Action和主要构造函数的不同arities的构造函数,您甚至可以避免使用toVar

答案 2 :(得分:0)

知道了。

/**
  * Template class for any SecureSocial SecuredAction that involves using user information.
  * @param isSuccessful: SecuredRequest[AnyContent] => Boolean
  */
class UserReqAction(
  isSuccessful: (SecuredRequest[AnyContent] => Boolean),
  success: Result, failure: Result) extends SecureSocial {
  def toReturn = SecuredAction(WithProvider("google")) {
    implicit request => if (isSuccessful(request)) success else failure
  }
}

/**
  * Template class for any SecureSocial SecuredAction that does not use user information.
  * @param isSuccessful: SecuredRequest[AnyContent] => Boolean
  */
class UserNotReqAction(isSuccessful: () => Boolean,
                       success: Result, failure: Result) extends SecureSocial {
  def toReturn = SecuredAction(WithProvider("google")) {
    implicit request => if (isSuccessful()) success else failure
  }
}

// not curried example
def makeNewThing = new UserReqAction(
  userOp.createThing,
  Ok(Json.toJson(Json.obj("success" -> true, "msg" -> "Thing successfully created"))),
  BadRequest("failed to create Thing")).toReturn

// curried example
def editThing(param1: _, param2: _, etc) = new UserReqAction(
  userOp.updateThing(param1, param2, etc), // this is the currying
  Ok(Json.toJson(Json.obj("success" -> true, "msg" -> "Thing successfully edited"))),
  BadRequest("failed to edit Thing")).toReturn

def updateThing(param1: _, param2: _, etc)(request: SecuredRequest[AnyContent] ) {...}