Implicit conversion to Runnable?
Asked Answered
S

5

18

As an exercise, I tried to create an implicit conversion that would accept a function and produce a Runnable. That way you could call Java methods that accept Runnable objects and use them like closures.

The implicit conversion is easy enough:

    implicit def funToRunnable(fun : Unit) = new Runnable() { def run = fun }

However I don't know how to call it. How do you pass in a no-arg function that returns Unit, without having it be evaluated at once? For example, I'd like the following to print "12" but instead it prints "21" because print("2") is evaluated at once.

    var savedFun : Runnable = null
    def save(r : Runnable) = { savedFun = r }

    save(print("2"))
    print("1")
    savedFun.run()

How do I tell the compiler to treat print("2") as the body of a function, not something to be evaluated at once? Some possibilities I tried, such as

    save(() => print("2"))

or

    save(=> print("2"))

are not legal syntax.

Simmon answered 18/6, 2010 at 23:28 Comment(1)
you meant "call java methods that accept Runnables and pass functions into them like closures"? because methods that accept other functions are not called closures; the functions they accept are called (sometimes) closures.Perjured
S
24

arg, just answered my own question. I implemented the implicit conversion incorrectly. The correct implementation is

implicit def funToRunnable(fun: () => Unit) = new Runnable() { def run() = fun() }

and you call it like this:

save(() => print("2"))

This will yield "2"

Simmon answered 18/6, 2010 at 23:40 Comment(5)
I was on the same track. Do you have any idea on why def run = run doesn't work?Holmberg
def run = run will always be an infinite recursion. Even if there's a run defined in an enclosing scope, the one defined by this def will shadow it, guaranteeing a direct, unconditional recursive call.Douzepers
I meant def run = fun without parensHolmberg
@Support because fun without parentheses just creates a method that returns the function that was passed. To call a function type, you have to use parentheses, otherwise you're just returning the funciton itself.Soup
Shouldnt it yield 2 instead of 12?Organdy
I
14

If you wanted to live dangerously, you could convert anything to a runnable:

implicit def whateverToRunnable[F](f: => F) = new Runnable() { def run() { f } }

scala> val t = new Thread(println("Hello"))
t: java.lang.Thread = Thread[Thread-2,5,main]

scala> t.start()
Hello

Or you could create your own thread-creator-and-starter:

def thread[F](f: => F) = (new Thread( new Runnable() { def run() { f } } )).start

scala> thread { println("Hi"); Thread.sleep(1000); println("Still here!") }
Hi

scala> Still here!

If you wanted to return the thread, then

def thread[F](f: => F) = {
  val t = new Thread( new Runnable() { def run() { f } } )
  t.start()
  t
}

But all of this, while useful, is perhaps even less useful than scala.actors.Futures (tested only on 2.8):

scala> import scala.actors.Futures

scala> val x = Futures.future { Thread.sleep(10000); "Done!" }
x: scala.actors.Future[java.lang.String] = <function0>

scala> x.isSet
res0: Boolean = false

scala> x.isSet
res1: Boolean = false

scala> x()   // Waits until the result is ready....
res2: java.lang.String = Done!
Incontinent answered 19/6, 2010 at 2:9 Comment(0)
H
5

Interesting, this way you can execute code that receives a Runnable and pass it a closure.

See:

scala> new Thread( ()  => print( "Hello" ) ).start()
<console>:5: error: overloaded method constructor Thread with alternatives (java.lang.ThreadGroup,java.lang.Runnable,java.lang.String,Long)java.lang.Thread <and> (java.lang.ThreadGroup,java.lang.Runnable,java.lang.String)java.lang.Thread <and> (java.lang.Runnable,java.lang.String)java.lang.Thread <and> (java.lang.ThreadGroup,java.lang.String)java.lang.Thread <and> (java.lang.String)ja...
       new Thread( ()  => print( "Hello" ) ).start()


scala> implicit def funcToRunnable( func : () => Unit ) = new Runnable(){ def run() = func() }
funcToRunnable: (() => Unit)java.lang.Object with java.lang.Runnable

scala> def doRun( runnable: Runnable ) = runnable.run
doRun: (Runnable)Unit

scala> doRun( () => print("Hola"))
Hola

scala> new Thread(()=>print("Hello")).start()

scala> Hello
Holmberg answered 18/6, 2010 at 23:54 Comment(0)
T
5

Actually, you can do it even nicer with call-by-name argument:

implicit def runnable(f: => Unit): Runnable = new Runnable() { def run() = f }

Usage:

import concurrent.ExecutionContext.Implicits.global._
execute(print("hello"))
Tight answered 20/4, 2015 at 21:30 Comment(0)
W
0

Yet another way to run some code in a different thread:

scala.actors.Actor.actor { ...doSomething()... }
Wolfie answered 3/5, 2012 at 11:54 Comment(1)
Runnable is not only used to run stuff in a newly Threads. For example in GUI development it is used to run stuff on the special UI thread.Newbill

© 2022 - 2024 — McMap. All rights reserved.