How Does One Make Scala Control Abstraction in Repeat Until? - scala

I am Peter Pilgrim. I watched Martin Odersky create a control abstraction in Scala. However I can not yet seem to repeat it inside IntelliJ IDEA 9. Is it the IDE?
package demo
class Control {
def repeatLoop ( body: => Unit ) = new Until( body )
class Until( body: => Unit ) {
def until( cond: => Boolean ) {
body;
val value: Boolean = cond;
println("value="+value)
if ( value ) repeatLoop(body).until(cond)
// if (cond) until(cond)
}
}
def doTest2(): Unit = {
var y: Int = 1
println("testing ... repeatUntil() control structure")
repeatLoop {
println("found y="+y)
y = y + 1
}
{ until ( y < 10 ) }
}
}
The error message reads:
Information:Compilation completed with 1 error and 0 warnings
Information:1 error
Information:0 warnings
C:\Users\Peter\IdeaProjects\HelloWord\src\demo\Control.scala
Error:Error:line (57)error: Control.this.repeatLoop({
scala.this.Predef.println("found y=".+(y));
y = y.+(1)
}) of type Control.this.Until does not take parameters
repeatLoop {
In the curried function the body can be thought to return an expression (the value of y+1) however the declaration body parameter of repeatUntil clearly says this can be ignored or not?
What does the error mean?

Here is a solution without the StackOverflowError.
scala> class ConditionIsTrueException extends RuntimeException
defined class ConditionIsTrueException
scala> def repeat(body: => Unit) = new {
| def until(condition: => Boolean) = {
| try {
| while(true) {
| body
| if (condition) throw new ConditionIsTrueException
| }
| } catch {
| case e: ConditionIsTrueException =>
| }
|
| }
| }
repeat: (body: => Unit)java.lang.Object{def until(condition: => Boolean): Unit}
scala> var i = 0
i: Int = 0
scala> repeat { println(i); i += 1 } until(i == 3)
0
1
2
scala> repeat { i += 1 } until(i == 100000)
scala> repeat { i += 1 } until(i == 1000000)
scala> repeat { i += 1 } until(i == 10000000)
scala> repeat { i += 1 } until(i == 100000000)
scala>
According to Jesper and Rex Kerr here is a solution without the Exception.
def repeat(body: => Unit) = new {
def until(condition: => Boolean) = {
do {
body
} while (!condition)
}
}

You don't need the 2nd pair of braces, the usage should be:
repeatLoop (x) until (cond) //or...
repeatLoop {x} until {cond}
And not:
repeatLoop {x} { until(cond) } //EXTRA PAIR OF BRACES
The error means that Scala thinks you are trying to call a method with a signature something like:
def repeatLoop(x: => Unit)(something: X) //2 parameter lists
And can find no such method. It is saying "repeatLoop(body)" does not take parameters. A full code listing for the solution probably looks something a bit more like:
object Control0 {
def repeatLoop(body: => Unit) = new Until(body)
class Until(body: => Unit) {
def until(cond: => Boolean) {
body;
val value: Boolean = cond;
if (value) repeatLoop(body).until(cond)
}
}
def main(args: Array[String]) {
var y: Int = 1
println("testing ... repeatUntil() control structure")
repeatLoop {
println("found y=" + y)
y += 1
}.until(y < 10)
}
}
There are two useful observations to make here:
The solution is not tail-recursive and will result in a StackOverflowError for long iterations (try while (y < 10000))
The until seems the wrong way round to me (it would be more natural to stop when the condition becomes true, not carry on while it is true).

How about a one liner for repeat until.
def repeat(b: => Unit) = new AnyRef {def until(c: => Boolean) {b; while (! c) b}}
Which, for example, gives:-
scala> repeat {
| println("i = "+i)
| i+=1
| } until (i >= 10)
i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9

As above yet recursive :)
def repeat(b: => Unit) = new {def until(c: => Boolean) = { b; if (c) until(c) }}
var i = 0
repeat {
println(i)
i+=1
} until (i < 10)
It's #tailrec optimized too.
Llove scala :)

Related

Problem in scala for-comprehension de-sugar?

I have the following for-comprehension which I de-sugared using command scala -Xprint:parser ForComprehensionDemo.scala. When I copy the de-sugared method and call it, it produces different result than the for-comprehension. Any idea why?
For comprehension:
object ForComprehensionDemo {
def main(args: Array[String]): Unit = {
forWithIf(true)
}
def forWithIf(condition: Boolean) = {
val x = for {
a <- name(0)
b <- if (condition) {
name(1)
name(2)
} else {
name(100)
}
c <- name(2)
} yield {
a + b + c
}
println(x)
}
def name(x: Int): Option[String] = {
println("called for :" + x)
x match {
case 0 => Some(" aa ")
case 1 => Some(" bb ")
case 2 => Some(" cc ")
case _ => Some(" not identified ")
}
}
}
Produces result:
called for :0
called for :1
called for :2
called for :2
Some( aa cc cc )
De-sugared code
def forWithIf(condition: Boolean) = {
val x = name(0).flatMap(((a) => if (condition)
{
name(1);
name(2)
}
else
name(100).flatMap(((b) => name(2).map(((c) => a.$plus(b).$plus(c)))))));
println(x)
};
Produces result:
called for :0
called for :1
called for :2
Some( cc )
Just a bug in the pretty printer. It's missing parens around the if-else.
In general, scala 2 doesn't represent parens very faithfully, but scala 3 is much better.
package desugar {
object ForComprehensionDemo extends scala.AnyRef {
def main(args: Array[String]): Unit = forWithIf(true);
def forWithIf(condition: Boolean) = {
val x = name(0).flatMap(((a) => (if (condition)
{
name(1);
name(2)
}
else
name(100)).flatMap(((b) => name(2).map(((c) => a.$plus(b).$plus(c)))))));
println(x)
};
def name(x: Int): Option[String] = {
println("called for :".$plus(x));
x match {
case 0 => Some(" aa ")
case 1 => Some(" bb ")
case 2 => Some(" cc ")
case _ => Some(" not identified ")
}
}
}
}
I was curious to see what Scala 3 says. -Xprint:all says after typer:
sugar.ForComprehensionDemo.name(0).flatMap[String](
{
def $anonfun(a: String): Option[String] =
(if condition then
{
sugar.ForComprehensionDemo.name(1)
sugar.ForComprehensionDemo.name(2)
}
else
{
sugar.ForComprehensionDemo.name(100)
}
).flatMap[String](
{
def $anonfun(b: String): Option[String] =
sugar.ForComprehensionDemo.name(2).map[String](
{
def $anonfun(c: String): String =
{
a.+(b).+(c)
}
closure($anonfun)
}
)
closure($anonfun)
}
)
closure($anonfun)
}
)
That's more inscrutible with the closures, but it has parens. Printing after parser isn't useful.
Just for comparison here is IntelliJ Desugar Scala code... output (with FQNs manually abbreviated)
def forWithIf(condition: Boolean) = {
val x = name(0)
.flatMap(((a) =>
(if (condition) {
name(1);
name(2)
} else {
name(100)
})
.flatMap(((b) =>
name(2)
.map(((c) => a.$plus(b).$plus(c)))))
));
println(x)
};
confirming som-snytt's answer.

Converting any method to retry-able method in scala

I am trying to implement a method that should take an arbitrary method or code block and it should convert the method or code-block to retry-able method.
Following example is intended to demonstrate what I need
import scala.util.{Try,Success,Failure}
object Retry {
retry[A, B](f: A => Try[B], r: Int): A => Try[B] = {
// return a function g and when g is invoked with parameters
// f should be tried (if failed) r number of time otherwise
// result of first successful execution of f should be returned
// from g. retry should work with any arbitrary function with
// any number/type of parameters
}
}
If you want to abstract over arity, which is pretty advanced, you'll have to use shapeless, a library for generic programming.
Building on #chengpohi's answer:
import shapeless._, ops.function._
import scala.util.Try
def retry[F, L <: HList, R](f: F, r: Int = 1)(implicit fnToP: FnToProduct.Aux[F, L => R], fnFromP: FnFromProduct.Aux[L => R, F]): F = {
val fn = fnToP(f)
def repeat(a: L): R = {
for (_ <- 0 to r) {
val tried = Try(fn(a))
if (tried.isSuccess) {
return tried.get
}
}
throw new RuntimeException(s"retry $r failed")
}
fnFromP(repeat _)
}
It works:
scala> var i = 0
i: Int = 0
scala> val f = retry( (a: Int) => if (i < 10) {i += 1; println(s"try $i"); throw new RuntimeException} else a * 3, 42)
f: Int => Int = shapeless.ops.FnFromProductInstances$$anon$2$$Lambda$1489/1404497488#1d49a23c
scala> f(5)
try 1
try 2
try 3
try 4
try 5
try 6
try 7
try 8
try 9
try 10
res4: Int = 15
scala> var i = 0
i: Int = 0
scala> val f = retry( (a: String, b: Int) => if (i < 10) {i += 1; println(s"try $i"); throw new RuntimeException} else a * b, 42)
f: (String, Int) => String = shapeless.ops.FnFromProductInstances$$anon$3$$Lambda$1492/121867201#1a22b89c
scala> f("foo", 5)
try 1
try 2
try 3
try 4
try 5
try 6
try 7
try 8
try 9
try 10
res5: String = foofoofoofoofoo
def retry[A, B](f: A => B, r: Int = 1): A => B = {
def repeat(a: A): B = {
for (_ <- 0 to r) {
val tried = Try(f(a))
if (tried.isSuccess) {
return tried.get
}
}
throw new RuntimeException(s"retry $r failed")
}
repeat
}
There is a simple way to do this, try if Success and return, otherwise throw Exception

Pattern matching java.lang.Long

I'm confused with scala pattern matching behaviour, see code below:
import java.util.concurrent.atomic.AtomicLong
object LongPatternMatching {
def main(args: Array[String]): Unit = {
useJavaLong
useScalaLong
useComplexJavaLong
}
def useScalaLong: Unit = {
val aLong: Long = Long.MaxValue
aLong match {
case v if v == Long.MinValue => println("min")
case v if v == Long.MaxValue => println("max")
}
}
def useJavaLong: Unit = {
val aLong: java.lang.Long = java.lang.Long.MAX_VALUE
aLong match {
case v if v == java.lang.Long.MIN_VALUE => println("min")
case v if v == java.lang.Long.MAX_VALUE => println("max")
}
}
def useComplexJavaLong: Unit = {
val counter: AtomicLong = new AtomicLong(0)
counter.incrementAndGet() match {
case count if count % 1000 == 0 => println(count)
}
}
}
First two functions are ok, but third(useComplexJavaLong) throws scala.MatchError: 1 (of class java.lang.Long)
useComplexJavaLong only matches a single case where the remainder of the modolu operation is 0. What happens if the remainder isn't equal to 0? You get a MatchError since there is no case which handles that. In the case of your example, 1 % 1000 is equal to 1, not 0, and thus the pattern match blows up. You'll need an additional case:
def useComplexJavaLong: Unit = {
val counter: AtomicLong = new AtomicLong(0)
counter.incrementAndGet() match {
case count if count % 1000 == 0 => println(count)
case count => println(s"Remainder equals: ${count % 1000}")
}
}
Because the pattern matching in the method useCompleJavaLong is not complete. You can change it to
def useComplexJavaLong: Unit = {
val counter: AtomicLong = new AtomicLong(0)
counter.incrementAndGet() match {
case count if count % 1000 == 0 => println(count)
case other => println(other)
}
}

how to return value from a if block in scala

I want to return an integer type from an if block like in this sample code:
def a {
val res = if (1 == 1) {
val x = 1
b(x)
}
}
def b(x:Int) = {
20
}
Here the type of res is val res: AnyVal
How can I change it to Int?
If you don't have a default value to return, then you could return an Option[Int] instead, and combine this with getOrElse:
def a(n: Int): Option[Int] = {
if (n < 100) {
Some(n * 2)
} else {
None
}
}
a(10).getOrElse("Something else")
Another possibility is to use partial functions, because in your case your first function does not cover all cases, and you want to have a fallback:
val a: PartialFunction[Int, Int] = {
case n if n < 100 =>
n * 2
}
val b: PartialFunction[Int, String] = {
case _ =>
"Something else"
}
Then you can use applyOrElse:
// If function a is not defined for the input, then call function b
val result = a.applyOrElse(10, b)
or combine both partial functions into another function, and call that one:
// Combine a and b
val cf = a.orElse(b)
// Call
val result = cf(10)
You have to add else alternative, for example:
def a {
val res = if (1 == 1) {
val x = 1
b(x)
} else 0
}
This happens because for absent else case compiler uses Unit. And nearest common type for Int and Unit is AnyVal.
The reason you didn't get a determined int is that: your if block miss some code path. So if you change to:
val res = if (i == 1) {
val x = 1
b(x)
} else {
0 // some default value
}
That will be fine.
To have an immutable value val res well-defined with an if-else expression, both parts of the expression need be declared, as aforementioned. Even in the case of a mutable variable
var res: Int = _
the compiler instantiates it to a default 0. Thereafter it is valid for instance to
if (1 == 1) res = 20
without having to define the else part.

Custom "let" expression in Scala

I'd love to have let construct similar to the one in Haskell in Scala. I tried a few ways, but none seems to be good. Here's some code:
object CustomLet extends App {
val data = for (i <- 1 to 1024; j <- 1 to 512) yield (i % j) * i * (i + 1) - 1
def heavyCalc() = { println("heavyCalc called"); data.sum }
def doSomethingWithRes(res: Int) = {
println(s"${res * res}")
1
}
def cond(value: Int): Boolean = value > 256
// not really usable, even though it's an expression (2x heavyCalc calls)
def withoutLet() = if (cond(heavyCalc())) doSomethingWithRes(heavyCalc()) else 0
// not an expression
def letWithVal(): Int = {
val res = heavyCalc()
if (cond(res)) doSomethingWithRes(res)
else 0
}
// a lot of code to simulate "let", at least it is an expression
def letWithMatch(): Int = heavyCalc() match {
case res => if (cond(res)) doSomethingWithRes(res) else 0
}
// not perfect solution from
// http://stackoverflow.com/questions/3241101/with-statement-equivalent-for-scala/3241249#3241249
def let[A, B](param: A)(body: A => B): B = body(param)
// not bad, but I'm not sure if it could handle more bindings at once
def letWithApp(): Int = let(heavyCalc()) {res => if (cond(res)) doSomethingWithRes(res) else 0}
List[(String, () => Int)](
("withoutLet", withoutLet),
("letWithVal", letWithVal),
("letWithMatch", letWithMatch),
("letWithApp", letWithApp)
).foreach(
item => item match {
case (title, func) => {
println(s"executing $title")
val ret = func()
println(s"$title finished with $ret")
println()
}
}
)
}
This is the ideal look of it (with only one binding, more could be separated by ,; not sure about the in keyword):
// desired look
def letTest(): Int =
let res = heavyCalc() in
if (cond(res)) doSomethingWithRes(res) else 0
I'm not sure if it's possible, but I have no experience with most of advanced Scala stuff like macros, so I can't really tell.
EDIT1: To be clear, the main things I'm expecting from it are: being expression and relatively simple syntax (like the one outlined above).
You could use a forward pipe:
object ForwardPipeContainer {
implicit class ForwardPipe[A](val value: A) extends AnyVal {
def |>[B](f: A => B): B = f(value)
}
}
to be used like this:
import ForwardPipeContainer._
def f(i: Int) = i * i
println( f(3) |> (x => x * x) )
You can put multiple arguments in a tuple:
println( (f(2), f(3)) |> (x => x._1 * x._2) )
which looks better if combined with partial function synatx:
println( (f(2), f(3)) |> { case (x, y) => x * y } )
This answer is a variation of What is a good way of reusing function result in Scala, and both are based on Cache an intermediate variable in an one-liner where I got the initial idea from.
def letTest(): Int =
let res = heavyCalc() in
if (cond(res)) doSomethingWithRes(res) else 0
I would write this:
def letTest(): Int = {
val res = heavyCalc()
if (cond(res)) doSomethingWithRes(res) else 0
}
Ignoring laziness, let is just a construct that introduces a lexical scope, binds some terms to some names then returns an expression. So in Scala you would do
{ // new lexical scope
// bind terms section
val a = f()
def b = a + g() // may be I don't want g to be evaluated unless b is needed
val c = h()
// result expression
if (c) b else a
}
Macros should be able to enforce this syntactic layout if you want to ensure that there is nothing else going on in the block. There is actually a SIP (Scala Improvement Process) proposal called Spores that would enforce some of the same constraints (and an additional one: that you don't capture a reference of an enclosing object unknowingly).
Note that blocks in Scala are expressions that evaluate to the last expression in the block. So let me take a random let example from Haskell:
aaa = let y = 1+2
z = 4+6
in let f = 3
e = 3
in e+f
This translates to:
val aaa = {
val y = 1 + 2
val z = 4 + 6
val u = {
val f = 3
val e = 3
e + f
}
u
}
As you can see the block statement can be used as an expression.