I have several methods which falsify a word, and I need to call them randomly, until one achieve to create a "human mistake".
I want to call a random method then another, until it's ok, but never twice the same.
Putting them in an array (or a list) imply that I rewrite an array each time I try a method, it's an ugly computational complexity and I'd like to write "Scala style" code, with minimum of var and mutability.
EDIT:
The Oleg Pyzhcov solution works well, but now I have some functions with String params, and other without. How to store functions and their call params in a collection?
val rand: Random = new Random()
def m1(a: String): Boolean = rand.nextBoolean()
def m2(): Boolean = rand.nextBoolean()
def m3(a: String, b: String): Boolean = rand.nextBoolean()
def m4(): Boolean = rand.nextBoolean()
def tryUntilOk(): Unit = {
def out = rand.shuffle(Stream(m1 _, m2 _, m3 _, m4 _))
.map(method => method()) // calling without params so error
.find(result => result) // stop when a method return true
}
EDIT 2:
DETAILS
I have several methods which tries to falsify a word, without guarantee that they achieve it. Some methods take the mood and tense of a verb, to change the tense or the mood, other just take the word correct writing to remove some letters, other take the gender and number of a noun to change it's gender.
I want to call a random method among all possible, and if it fail to falsify the word (for example the given noun only exist in the feminine form) then call another randomly. Repeating this operation until no more methods are available, so we give up.
The solution of Oleg is nice for the random part, but I can't find how to give to methods call parameters.
Concrete exemple:
package Counterfeiters
import Words.Noun
object Noun extends Counterfeiter[Noun] {
override def counterfeit(word: Noun): Unit = {
// For now I call methods without random order
// This one take only one String param
// And split letters that are duplicated like boot -> bot
word.currentWriting = splitDoubleLetters(word.correctWriting)
// Then we compare word.currentWriting and word.correctWriting
// If the word wasn't containing double letters, it isn't counterfeited
if (!word.isCounterfeited)
// This one take 5 String params
// And counterfeit the gender or the number of the word, randomly
word.currentWriting = counterfeitGenderNumberWord("N", word.correctWriting, word.lemma, word.gender, word.number)
}
}
To apply the solution of Oleg, I just need to find how to store methods in a collection, with corresponding params. In this case (splitDoubleLetters, (word.correctWriting)) and (counterfeitGenderNumberWord, ("N", word.correctWriting, word.lemma, word.gender, word.number)).
SOLUTION
I did what Oleg advised in a comment:
object Noun extends Counterfeiter[Words.Noun] {
override def counterfeit(word: Words.Noun): Unit = {
if (word.isCounterfeited) return
def split: () => String = () => splitDoubleLetter(word.correctWriting)
def ctftGenderNumber: () => String = () => counterfeitGenderNumberWord("N", word.correctWriting, word.lemma, word.gender, word.number)
val methods: immutable.Seq[() => String] = Stream(split, ctftGenderNumber)
val res: Option[String] = randomizer.shuffle(methods) // Shuffle methods
.map(method => method()) // Call them one by one
.find(result => result != word.correctWriting) // Until one counterfeit the word
word.currentWriting = res match {
case None => word.correctWriting // If every methods failed
case _ => res.get
}
}
}
SergGr explained well a possible architecture, I'll fit to this as it's clear.
You can find my complete project code on GitHub if you want to understand better what I do.
Using Stream for laziness and Random#shuffle method you can get:
import scala.util.Random
def m1(): Boolean = Random.nextBoolean()
def m2(): Boolean = Random.nextBoolean()
def m3(): Boolean = Random.nextBoolean()
def m4(): Boolean = Random.nextBoolean()
def out = Random.shuffle(Stream(m1 _, m2 _, m3 _, m4 _))
.map(method => method()) // pass in any necessary params
.find(result => !result) // do your check
Here, out has type Option[Boolean] since methods m1-m4 all return Boolean
I agree with Oleg that what you need is to convert all you methods into a collection of the same shape. I assume that in the word package you have base class Word with subclasses for different parts of the speech with different characteristics. Something like this:
abstract class Word(val correctWriting: String, var currentWriting: String, val lemma: String) {
def isCounterfeited: Boolean = !correctWriting.equals(currentWriting)
}
sealed trait Gender
case object Masculine extends Gender
case object Feminine extends Gender
case object Neutral extends Gender
sealed trait Number
case object Singular extends Number
case object Plural extends Number
class Noun(correctWriting: String, currentWriting: String, lemma: String, val gender: Gender, val number: Number) extends Word(correctWriting, currentWriting, lemma) {
}
and you have trait Counterfeiter defined as
trait Counterfeiter[-W <: Word] {
def counterfeit(word: W): Unit
}
Then you may define helper class RandomCompoundCounterfeiter
type SimpleCounterfeiter[W <: Word] = (String, W) => String
class RandomCompoundCounterfeiter[W <: Word](val children: Seq[SimpleCounterfeiter[W]]) extends Counterfeiter[W] {
override def counterfeit(word: W): Unit = {
Random.shuffle(children).takeWhile(c => {
word.currentWriting = c(word.correctWriting, word)
!word.isCounterfeited
})
}
}
RandomCompoundCounterfeiter is the class the does the main job you were asking for: it applies other SimpleCounterfeiter in a random order. It does this by first shuffling the list of children (i.e. real counterfeiters) and applying them until after some word.isCounterfeited is finally true or the list is exhausted.
Note that RandomCompoundCounterfeiter re-shuffles counterfeiters on each call. If you want to have your order to be different between different runs of the application but the same for different words inside single run, just move the shuffling to the constructor.
Now define list of basic SimpleCounterfeiters functions such as
object Noun {
val singularCounterfeiter = (correctWriting: String, word: Noun) => {
if (word.number == Singular)
correctWriting
else
???
}
val pluralCounterfeiter = (correctWriting: String, word: Noun) => {
if (word.number == Plural)
correctWriting
else
???
}
def genderCounterfeiter(newGender: Gender): SimpleCounterfeiter[Noun] = (correctWriting: String, word: Noun) => {
if (word.gender == newGender)
correctWriting
else
???
}
val all = List(
GenericCounterfeiters.splitDoubleLetters,
singularCounterfeiter,
pluralCounterfeiter,
genderCounterfeiter(Neutral),
genderCounterfeiter(Masculine),
genderCounterfeiter(Feminine))
val nounCounterfeiter = new RandomCompoundCounterfeiter[Noun](all)
}
Now you can use Noun.nounCounterfeiter as your random Counterfeiter[Noun]. The main idea here is to have the same shape for each atomic counterfeiter and this is achieved by always passing the whole Word (or its subclass) to the method. So now each method has access to all the relevant information if it needs some.
If you prefer to move the typical if condition in counterfeiters to a single place, you may refactor your code to a bit more OOP-way:
class RandomCompoundCounterfeiter[W <: Word](val children: Seq[Counterfeiter[W]]) extends Counterfeiter[W] {
override def counterfeit(word: W): Unit = {
Random.shuffle(children).takeWhile(c => {
c.counterfeit(word)
!word.isCounterfeited
})
}
}
trait SimpleCounterfeiter[-W <: Word] extends Counterfeiter[W] {
override def counterfeit(word: W): Unit = {
if (isApplicable(word))
word.currentWriting = counterfeitImpl(word.correctWriting, word)
}
def isApplicable(word: W): Boolean
def counterfeitImpl(correctWriting: String, word: W): String
}
object GenericCounterfeiters {
val splitDoubleLetters = new SimpleCounterfeiter[Word] {
override def isApplicable(word: Word) = true
override def counterfeitImpl(correctWriting: String, word: Word) = ???
}
}
object Noun {
val singularCounterfeiter = new SimpleCounterfeiter[Noun] {
override def isApplicable(word: Noun) = word.number != Singular
override def counterfeitImpl(correctWriting: String, word: Noun) = ???
}
val pluralCounterfeiter = new SimpleCounterfeiter[Noun] {
override def isApplicable(word: Noun) = word.number != Plural
override def counterfeitImpl(correctWriting: String, word: Noun) = ???
}
def genderCounterfeiter(newGender: Gender) = new SimpleCounterfeiter[Noun] {
override def isApplicable(word: Noun) = word.gender != newGender
override def counterfeitImpl(correctWriting: String, word: Noun) = ???
}
val all = List(
GenericCounterfeiters.splitDoubleLetters,
singularCounterfeiter,
pluralCounterfeiter,
genderCounterfeiter(Neutral),
genderCounterfeiter(Masculine),
genderCounterfeiter(Feminine))
val nounCounterfeiter = new RandomCompoundCounterfeiter[Noun](all)
}
Related
Just trying to propagate my tracing context inside Kleisli as it was done originally in the next tutorial.
object TraceLogger {
def log(msg: String): Kleisli[IO, UUID, Unit] = Kleisli { traceId => IO(println(s"[$traceId] $msg")) }
}
trait ServiceStub {
def request(arg: String): Kleisli[IO, UUID, _]
}
trait ClientStub {
def get(arg: String): Kleisli[IO, UUID, _]
}
case class FirstServiceExample(clientStub: ClientStub) extends ServiceStub {
override def request(arg: String): Kleisli[IO, UUID, _] = Kleisli { (context: UUID) =>
val requestComputation = clientStub.get("calling second service!")
TraceLogger.log(arg)
requestComputation(context)
}
}
case class FirstClientExample(service: FirstServiceExample) {
def request(): IO[_] = {
val traceId = UUID.randomUUID()
service.request("root!").run(traceId)
}
}
And now I need to run execution:
val exampleClientStub = new ClientStub() {
override def get(arg: String): Kleisli[IO, UUID, _] = Kleisli.ask
}
val exampleClientService = FirstServiceExample(exampleClientStub)
FirstClientExample(exampleClientService).request().unsafeRunSync()
But, unfortunately, I don't see any logs here. Would you kindly help me to find an issue?
TraceLogger.log(arg) This returns an IO which is just a description of computation; it is doing nothing.
And since you just leave that value alone it is equivalent to just having a 1 in the middle of your code, it is simply discarded.
You need to chain your IOs together to create new IOs that represent "do this and then do that", that is basically what the flatMap method does.
Kleisli { (context: UUID) =>
val requestComputation = clientStub.get("calling second service!")
TraceLogger.log(arg)(context) >> // >> is equivalent to flatMap(_ => )
requestComputation(context)
}
(There is probably a better way to write this, I am not used to Kliesli)
Fabio's series on "Programas as Values" may be very useful: https://systemfw.org/archive.html
It's been while Since I've started working on scala and I am wondering what kind of variable type is the best when I create a method which requires to return multiple data.
let's say If I have to make a method to get user info and it'll be called from many places.
def getUserParam(userId: String):Map[String,Any] = {
//do something
Map(
"isExist" -> true,
"userDataA" -> "String",
"userDataB" -> 1 // int
)
}
in this case, the result type is Map[String,Any] and since each param would be recognized as Any, You cannot pass the value to some other method requiring something spesifically.
def doSomething(foo: String){}
val foo = getUserParam("bar")
doSomething(foo("userDataA")) // type mismatch error
If I use Tuple, I can avoid that error, but I don't think it is easy to guess what each indexed number contains.
and of course there is a choice to use Case Class but once I use case class as a return type, I need to import the case class where ever I call the method.
What I want to ask is what is the best way to make a method returning more than 2 different variable type values.
Here are three options. Even though you might like the third option (using anonymous class) it's actually my least favorite. As you can see, it requires you to enable reflective calls (otherwise it throws a compilation warning). Scala will use reflection to achieve this which is not that great.
Personally, if there are only 2 values I use tuple. If there are more than two I will use a case class since it greatly improves code readability. The anonymous class option I knew it existed for a while, but I never used that it my code.
import java.util.Date
def returnTwoUsingTuple: (Date, String) = {
val date = new Date()
val str = "Hello world"
(date,str)
}
val tupleVer = returnTwoUsingTuple
println(tupleVer._1)
println(tupleVer._2)
case class Reply(date: Date, str: String)
def returnTwoUsingCaseClass: Reply = {
val date = new Date()
val str = "Hello world"
Reply(date,str)
}
val caseClassVer = returnTwoUsingCaseClass
println(caseClassVer.date)
println(caseClassVer.str)
import scala.language.reflectiveCalls
def returnTwoUsingAnonymousClass = {
val date = new Date()
val str = "Hello world"
new {
val getDate = date
val getStr = str
}
}
val anonClassVer = returnTwoUsingAnonymousClass
println(anonClassVer.getDate)
println(anonClassVer.getStr)
Sinse your logic with Map[String,Any] is more like for each key I have one of .. not for each key I have both ... more effective use in this case would be Either or even more effectively - scalaz.\/
scalaz.\/
import scalaz._
import scalaz.syntax.either._
def getUserParam(userId: String): Map[String, String \/ Int \/ Boolean] = {
//do something
Map(
"isExist" -> true.right,
"userDataA" -> "String".left.left,
"userDataB" -> 1.right.left
)
}
String \/ Int \/ Boolean is left-associatited to (String \/ Int) \/ Boolean
now you have
def doSomething(foo: String){}
unluckily it's the most complex case, if for example you had
def doSomethingB(foo: Boolean){}
you could've just
foo("userDataA").foreach(doSomethingB)
since the right value considered as correct so for String which is left to the left you could write
foo("userdata").swap.foreach(_.swap.foreach(doSomething))
Closed Family
Or you could craft you own simple type for large number of alternatives like
sealed trait Either3[+A, +B, +C] {
def ifFirst[T](action: A => T): Option[T] = None
def ifSecond[T](action: B => T): Option[T] = None
def ifThird[T](action: C => T): Option[T] = None
}
case class First[A](x: A) extends Either3[A, Nothing, Nothing] {
override def ifFirst[T](action: A => T): Option[T] = Some(action(x))
}
case class Second[A](x: A) extends Either3[Nothing, A, Nothing] {
override def ifSecond[T](action: A => T): Option[T] = Some(action(x))
}
case class Third[A](x: A) extends Either3[Nothing, Nothing, A] {
override def ifThird[T](action: A => T): Option[T] = Some(action(x))
}
now having
def getUserParam3(userId: String): Map[String, Either3[Boolean, String, Int]] = {
//do something
Map(
"isExist" -> First(true),
"userDataA" -> Second("String"),
"userDataB" -> Third(1)
)
}
val foo3 = getUserParam3("bar")
you can use your values as
foo3("userdata").ifSecond(doSomething)
More specifically, I have:
case class Key (key: String)
abstract class abstr {
type MethodMap = PartialFunction[Key, String => Unit]
def myMap: MethodMap // abstract
def useIt (key: Key, value: String) = {
val meth = myMap(key)
meth(value)
}
def report = {
for (key <- myMap.keySet) // how to do this
println("I support "+key)
}
}
I use it like this:
class concrete extends abstr {
var one: Boolean
def method1(v: String): Unit = ???
def method2(v: String): Unit = ???
def map1: MethodMap = {
case Key("AAA") => method1
}
def map2: MethodMap = {
case Key("AAA") => method2
}
override def myMap: MethodMap = if (one) map1 else map2
}
Of course, this is somewhat simplified, but the report function is necessary.
Some history: I first had it implemented using Map but then I changed it to PartialFunction in order to support the following override def myMap: MethodMap = if (one) map1 else map2.
Any suggestion to refactor my code to support everything is also appreciated.
No. PartialFunction can be defined (and often is) on infinite sets. E.g. what do you expect report to return in these situations:
class concrete2 extends abstr {
def myMap = { case Key(_) => ??? }
}
or
class concrete2 extends abstr {
def myMap = { case Key(key) if key.length > 3 => ??? }
}
? If you have a finite list of values you are interested in, you can do
abstract class abstr {
type MethodMap = PartialFunction[Key, String => Unit]
def myMap: MethodMap // abstract
val keys: Seq[Key] = ...
def report = {
for (key <- keys if myMap.isDefined(key))
println("I support "+key)
}
}
Some history: I first had it implemented using Map but then I changed it to PartialFunction in order to support the last line in second part.
Why? This would work just as well with Map.
In your solution, is there any way to define the domain of the partial function to be the finite set keys
def f: MethodMap = { case key if keys.contains(key) => ... }
Of course, the domain isn't part of the type.
Inspired by my C# implementation of generic Strategy pattern I want to do the same in Scala. I want also make some functional programming to encapsulate Strategy algorithms inside a inherited class.
So what I've done yet is:
trait Strategy {
type T <: Strategy
type O
def Call(victim: T): O = {
strategy(victim)
}
var strategy: (this.T => this.O)
}
This is a trait which is a base of scaldfolding. I have also a StrategyFactory class:
case class StrategyFactory[T <: Strategy, O](str: T) {
def Call(x: (T => O)) = x(str)
}
object StrategyFactory {
}
And finally in my code I can create concrete Strategy:
class DownloadStrategy(path: String) extends Strategy {
type T = DownloadStrategy
type O = String
strategy = (dw: DownloadStrategy) => path + "aaaa"
}
object DownloadStrategy {
def apply(s: String) = new DownloadStrategy(s)
}
In my application code I have this:
var ds = DownloadStrategy("j")
val m = StrategyFactory[DownloadStrategy, String](ds)
var output = m.Call(ds.strategy)
Here works everything good.
I want to have functional strategies thus there is m.Call(ds.strategy)
But It is very dummy design because I cannot create a set of classes which will be extending DownloadStrategy. For example:
class ImageDownloadStrategy(w: String, h: String, path: String) extends DownloadStrategy(path){
type T = ImageDownloadStrategy
type O = String
strategy = (ids: T) => path + ":ImageDownloadStrategy"
}
class VideoDownloadStrategy(w: String, h: String, path: String) extends DownloadStrategy(path){
type T = VideoDownloadStrategy
type O = String
strategy = (ids: T) => path + ":VideoDownloadStrategy"
}
And so on. Basically I want to have one base class of some default strategy and subclasses are more specific implementations.
This brings me to application code where I would like to code something like this:
var ds: DownloadStrategy = null
request.getQueryString("t") match {
case "1" => ds = ImageDownloadStrategy("","","")
case "2" => ds = VideoDownloadStrategy("","","")
case "3" => ds = RawFileDownloadStrategy("","","")
case _ => ds = DownloadStrategy("")
}
var output = (StrategyFactory[DownloadStrategy, String](ds)).Call(ds.strategy)
I thought that when I write StrategyFactory[DownloadStrategy, String](ds) the compiler will be so smart enought that can figure if ImageDownloadStrategy is subclass of DownloadStrategy will could allow me to do some polimorphic calls but i cannot do it.
Another fact is that I need to overrides type T and type O in delivered class from DownloadStrategy but I dont have any idea how to do it.
Please give me some advices how to model this kind of behaviour.
EDIT(for pagoda_5b details)
As I have mentioned I have functional var strategy in trait Strategy which is var strategy: (this.T => this.O). This variable need to be overriden in classes implementing this trait. Also I have 2 generic types which T means subclass of concrete strategy and O indicate result type from def Call(...).
What I want to achive is having functional strategies inside subclass of Strategy and then make polimorphic calls. Here I have got DownloadStrategy which is default strategy and I have some subclasses with specicif algorithms. I want cast ImageDownloadStrategy to DownloadStrategy and use it as I showed in switch case statement.
Ok, I'll try to take a shot.
Since you can have function objects, you probably can simply do without any of the machinery of a Strategy hierarchy or factory whatsoever.
You can for example
//this is sort of a factory
object Strategies {
//a type alias to better define your selected functions
type Strategy[T, O] = T => O
//a set of methods to obtain the correct strategy "on demand"
def imageDownload[T](w: String, h: String, path: String): Strategy[T, String] =
(t: T) =>
path + ":ImageDownloadStrategy"
def videoDownload[T](w: String, h: String, path: String): Strategy[T, String] =
(t: T) =>
path + ":VideoDownloadStrategy"
def rawFileDownload[T](w: String, h: String, path: String): Strategy[T, String] =
(t: T) =>
path + ":RawDownloadStrategy"
//this is the fallback default
def download[T](path: String): Strategy[T, String] =
(t: T) =>
path + "aaaa"
}
object Client {
//make the strategies visible
import Strategies._
//processes the request
def process(request: Request): String = {
//here val means that the strategy variable won't be reassigned, ever
val strategy = selectStrategy[T](request.getQueryString("t")) //here we miss the type of the input value
//this assignment could be omitted if it's just returned
val output = strategy(??) //Here I'm missing the input to the strategy
output
}
//a method to select the strategy to use
def selectStrategy[T](selector: String): Strategy[T, String] =
selector match {
case "1" => imageDownload("","","")
case "2" => videoDownload("","","")
case "3" => rawFileDownload("","","")
case _ => download("")
}
}
As you can see, I'm missing what is the input value passed from the request to the strategy, so there are a couple holes in the process method
I don't know if this is what you need, but it could give you an idea why the strategy pattern is not so useful in functional languages, but rather needlessly cumbersome.
EDIT
Finally I found time to post real life example of downloading strategy in playframework.
object Download{
object Type extends Enumeration {
type Type = Value
val Image = "1"
val Video = "2"
val Pdf = "3"
val File = "4"
}
}
object Strategies {
type Strategy[T, O] = T => O
def imageDownload[T](): Strategy[T, java.io.File] =
(t: T) => {
//Receive download strategy information
val dw = t.asInstanceOf[DownloadStrategy]
//juicy code goes here
java.io.File.createTempFile("", "")
}
def videoDownload[T](): Strategy[T, java.io.File] =
(t: T) =>
java.io.File.createTempFile("", "")
def rawFileDownload[T](): Strategy[T, java.io.File] =
(t: T) =>
java.io.File.createTempFile("", "")
//this is the fallback default
def download[T](): Strategy[T, java.io.File] =
(t: T) => {
java.io.File.createTempFile("", "")
}
//a method to select the strategy to use
def selectStrategy[T](selector: String): Strategy[T, java.io.File] =
selector match {
case Download.Type.Image => {
imageDownload()
}
case Download.Type.Video => {
videoDownload()
}
case Download.Type.Pdf => {
rawFileDownload()
}
case Download.Type.File => {
rawFileDownload()
}
case _ => download()
}
}
case class DownloadStrategy(request: Request[AnyContent], path: String, file: Option[File]) {
}
//Controller code
def download(path: String) = Action {
implicit request =>
val file: Option[File] = FileStore.byPath(path, true)
val ds = DownloadStrategy(request, path, file)
//request.getQueryString("t") - Download type
val str = Strategies.selectStrategy[DownloadStrategy](request.getQueryString("t").getOrElse(""))
val x = str(ds)
Ok.sendFile(
content = x
)
}
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 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.