开发者

How to return optional information from methods?

开发者 https://www.devze.com 2023-03-14 23:26 出处:网络
The general question is how to return additional information from methods, beside the actual result of the computation. But I want, that this information can silently be ignored.

The general question is how to return additional information from methods, beside the actual result of the computation. But I want, that this information can silently be ignored.

Take for example the method dropWhile on Iterator. The returned result is the mutated iterator. But maybe sometimes I might be interested in the number of elements dropped.

In the case of 开发者_Python百科dropWhile, this information could be generated externally by adding an index to the iterator and calculating the number of dropped steps afterwards. But in general this is not possible.

I simple solution is to return a tuple with the actual result and optional information. But then I need to handle the tuple whenever I call the method - even if I'm not interested in the optional information.

So the question is, whether there is some clever way of gathering such optional information?

Maybe through Option[X => Unit] parameters with call-back functions that default to None? Is there something more clever?


Just my two cents here…

You could declare this:

case class RichResult[+A, +B](val result: A, val info: B)

with an implicit conversion to A:

implicit def unwrapRichResult[A, B](richResult: RichResult[A, B]): A = richResult.result

Then:

def someMethod: RichResult[Int, String] = /* ... */

val richRes = someMethod
val res: Int = someMethod


It's definitely not more clever, but you could just create a method that drops the additional information.

def removeCharWithCount(str: String, x: Char): (String, Int) =
  (str.replace(x.toString, ""), str.count(x ==))

// alias that drops the additional return information
def removeChar(str: String, x: Char): String =
  removeCharWithCount(str, x)._1


Here is my take (with some edits with a more realistic example):

package info {
  trait Info[T] { var data: Option[T] }
  object Info {
    implicit def makeInfo[T]: Info[T] = new Info[T] { 
      var data: Option[T] = None 
    }
  }
}

Then suppose your original method (and use case) is implemented like this:

object Test extends App {
  def dropCounterIterator[A](iter: Iterator[A]) = new Iterator[A] {
    def hasNext = iter.hasNext
    def next() = iter.next()
    override def dropWhile(p: (A) => Boolean): Iterator[A] = {
      var count = 0
      var current: Option[A] = None
      while (hasNext && p({current = Some(next()); current.get})) { count += 1 }
      current match {
        case Some(a) => Iterator.single(a) ++ this
        case None => Iterator.empty
      }
    }
  }

  val i = dropCounterIterator(Iterator.from(1))
  val ii = i.dropWhile(_ < 10)
  println(ii.next())
}

To provide and get access to the info, the code would be modified only slightly:

import info.Info // line added

object Test extends App {
  def dropCounterIterator[A](iter: Iterator[A]) = new Iterator[A] {
    def hasNext = iter.hasNext
    def next() = iter.next()
    // note overloaded variant because of extra parameter list, not overriden
    def dropWhile(p: (A) => Boolean)(implicit info: Info[Int]): Iterator[A] = {
      var count = 0
      var current: Option[A] = None
      while (hasNext && p({current = Some(next()); current.get})) { count += 1 }
      info.data = Some(count) // line added here
      current match {
        case Some(a) => Iterator.single(a) ++ this
        case None => Iterator.empty
      }
    }
  }

  val i = dropCounterIterator(Iterator.from(1))
  val info = implicitly[Info[Int]] // line added here
  val ii = i.dropWhile((x: Int) => x < 10)(info) // line modified
  println(ii.next())
  println(info.data.get) // line added here
}

Note that for some reason the type inference is affected and I had to annotate the type of the function passed to dropWhile.


You want dropWhileM with the State monad threading a counter through the computation.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号