2

Suppose I have an API set up to accept an implicit context.

def foo(i : Int)(implicit ctx : Context) : Int = ???
def bar(s : String)(implicit ctx : Context) : Int = ???

Applications might require multiple contexts. It's not always appropriate to just import of widely used value. But contexts are annoying to create, and require some cleanup when they will no longer be used. So, I want to define an API like this:

withContext( m : Mood, w : Weather ) {
  val f = foo(7)
  val b = bar("Boo!")
  f + b
}

Within my code block, an implicit context would be set. At the end, it would be cleaned up.

I thought this would be easy, but I've quickly confused myself.

I try something like

def withContext[R]( m : Mood, w : Weather )( op : Context => R ) : R = {
   implicit val context = FeelyContext(m,w)
   try op( context ) finally context.close()
}

but that doesn't work. I'd still need to write

withContext( m : Mood, w : Weather ) { context =>
  val f = foo(7)( context )
  val b = bar("Boo!")( context )
  f + b
}

which is not so convenient.

Is there a way to do this in ordinary scala? Would it need to be a macro trick, since implicit resolution happens at compile time?

Update: It occurs to me that it should work to write

withContext( m : Mood, w : Weather ) { implicit context =>
  val f = foo(7)
  val b = bar("Boo!")
  f + b
}

which is not so bad. Still, is there any way to do without the declaration of context entirely?

Steve Waldman
  • 13,689
  • 1
  • 35
  • 45
  • This does not seem like a valid scala code to me. You have an argument `Context => R`, but what you are trying to pass there is not an object. Have you tried running your example in REPL? – Archeg Apr 04 '16 at 18:06
  • I'm passing a function, that accepts a context and returns an R (generic so that the construct can return anything). That part works fine, see e.g. http://stackoverflow.com/questions/20762240/loan-pattern-in-scala – Steve Waldman Apr 04 '16 at 18:09
  • But why do you have two method definitions there? These two are not expression in scala, and thus this code does not compile. Note that in the link you've posted there are no `def`s – Archeg Apr 04 '16 at 18:11
  • Just a guess: maybe you wanted something like `=> new { def f = ... def b = ...}`? – Archeg Apr 04 '16 at 18:14
  • It does compile. For a quick example, try `import java.io._; def withOutputStream[R]( fname : String )( op : OutputStream => R ) : R = { implicit val os = new FileOutputStream( fname ); try op(os) finally os.close }` An `OutputStream` is not a great context object for a real API, but if you define a method like `def greet(name : String)(implicit os : OutputStream) = os.write( s"Hello ${name}!".getBytes )`, you can play with the problem space in a REPL. – Steve Waldman Apr 04 '16 at 18:15
  • Oh! I see what you are reacting to! I wrote `def` where I meant `val` in the question! Thanks! I'm an idiot. Let me fix that! – Steve Waldman Apr 04 '16 at 18:16
  • Sorry about that!!! Is it clearer now? – Steve Waldman Apr 04 '16 at 18:18
  • Unfortunately no. `val a = ...` is still not an expression. Please try compiling your code before posting it here – Archeg Apr 04 '16 at 18:20
  • OK. You are right, they'd not compile as-is. [Update: actually, they do] They are expressions now. This is pseudocode, my real use case is more complicated, sorry about the missing expression values. Clearer now? – Steve Waldman Apr 04 '16 at 18:22
  • (Actually, checking, they do compile with straight val statements, the expression value is Unit. But I'll leave in the `f+b` expression values so this doesn't come up again.) – Steve Waldman Apr 04 '16 at 18:25
  • You are right, I did not expect scala to infer `Unit`, but it does, sorry. – Archeg Apr 04 '16 at 18:36
  • we are both right, we are both wrong, let's have an apology tour together! in any case, thank you for taking a look! – Steve Waldman Apr 04 '16 at 18:37
  • No problem. Unfortunately I don't think I can answer this, - your code (the last one) seems to be the best you can do. You probably could do more with macros, but I would strongly advise not to do that - in this state you have your code is quite simple and very readable. It won't be if you use macros and I don't think it's worth it – Archeg Apr 04 '16 at 18:39
  • Play does exactly the same to make implicit request available for you so I would asume that is a valid solution. – Łukasz Apr 04 '16 at 18:39
  • @Archeg I probably agree that a macro would be more trouble than it would be worth. I was just surprised that I couldn't find a way to write what I originally intended, I thought it should be possible. But "implicit context =>" is not in the scheme of things so much boilerplate. Thank you again. – Steve Waldman Apr 04 '16 at 18:42
  • @Łukasz Thanks. That's right. Here's Play quite similar to my **Update** version here: https://www.playframework.com/documentation/2.6.x/ScalaActions – Steve Waldman Apr 04 '16 at 18:44
  • `withSomething { implicit x => ... }` is absolutely idiomatic in scala, that's how you should do it – Giovanni Caporaletti Apr 04 '16 at 20:28

0 Answers0