【问题标题】:Partially applying a function that has an implicit parameter部分应用具有隐式参数的函数
【发布时间】:2013-05-07 08:05:53
【问题描述】:

我可以把一个带有隐式参数的方法变成一个函数吗?

trait Tx

def foo(bar: Any)(implicit tx: Tx) {}

foo _ // error: could not find implicit value for parameter tx: Tx

我正在尝试实现以下目标,如果我能以某种方式使其与普通调用 withSelection(deleteObjects) 一起工作,则最好:

trait Test {      
  def atomic[A](fun: Tx => A): A

  def selection: Iterable[Any]

  def withSelection(fun: Iterable[Any] => Tx => Unit) {
    val sel = selection
    if (sel.nonEmpty) atomic { implicit tx =>
      fun(sel)(tx)
    }
  }

  object deleteAction {
    def apply() {
      withSelection(deleteObjects)  // !
    }
  }

  def deleteObjects(xs: Iterable[Any])(implicit tx: Tx): Unit
}

我找到了this question,但据我所知,它并没有处理从方法到函数的提升。

【问题讨论】:

    标签: scala implicit currying partial-application


    【解决方案1】:

    隐式仅适用于方法。但是你必须将一个函数传递给withSelection。您可以通过将方法包装在函数中来解决:

    withSelection(a => b => deleteObjects(a)(b))
    

    不可能直接传递deleteObjects,因为foo _不适用于定义了隐式参数列表的foo

    【讨论】:

      【解决方案2】:

      据我所知,隐式解析必须在使用现场进行,并且不能被规避。我自己的失望时刻是当我试图解决我的代码中的“ExecutionContext”扩散问题时。

      我一直在考虑的一个折衷方案是:

      type Async[A] = ExecutionContext => Future[A]
      
      def countFiles(root: String): Async[Int] = implicit ec =>
        // ...
      

      “隐式”只存在于函数中——我们必须在调用上妥协:

      implicit class AsyncExt[A](async: Async[A]) {
        def invoke()(implicit ec: ExecutionContext) = async(ec)
      }
      
      implicit val ec = ...
      countFiles("/").invoke()
      

      另一种妥协——我选择并活到后悔的那个:

      class AsyncFileCounter(ec: ExecutionContext) {
        def countFiles(root: String): Future[A] = ...
      }
      
      class FileCounter {
        def async(implicit ec: ExecutionContext) = new AsyncFileCounter(ec)
      }
      

      这改变了天真的用法(但需要):

      implicit val ec = ...
      val counter = new FileCounter
      counter.countFiles("/") // <-- nope
      

      致以下:

      implicit val ec = ...
      val counter = new FileCounter
      counter.async.countFiles("/") // yep!
      

      根据您的上下文,这可能是可以忍受的。你可以在我使用“def async”的地方添加一个“def transactional”。

      我确实对此感到遗憾,因为它使继承复杂化,并产生一些分配开销(尽管应该 JIT 去掉)。

      底线是您必须想出一种更明确的零碎方法来调用您的函数——这种方法不如单独进行柯里化。

      【讨论】:

      • 关于你的第二种情况(即FileCounter),你不能定义一个隐式转换implicit def counterToAsync(c: FileCounter): AsyncFileCounter = c.async,这样你就可以再次counter.countFiles("/")吗?
      • 不错!在我的情况下,隐式转换将被包含对象上存在的具有类似签名的同步方法所击败。但是,这绝对是一个很好的样板解决方法!不过,我确实需要验证逃逸分析确实消除了 Async 对象的堆分配。
      • 我想说同步方法会有不同的签名,因为它们不会返回Future[A],而只是返回A,所以编译器可以告诉你。您应该简单地划定需要异步调用的范围并在那里导入隐式转换。至于避免堆分配,我不能打赌......
      • 唉,Scala 不支持仅基于返回值的方法重载。尽管如此,您建议的隐式转换在许多情况下都很有用。关于堆分配——一旦 JIT 确定“异步”对象永远不会离开堆栈帧(它不应该),那么 JIT 将确保它只会分配给堆栈而不是堆。
      • 感谢您的解释
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-23
      相关资源
      最近更新 更多