Search code examples
scalacurryingpartialfunction

Partial Functions and Execute-Once behaviour in Scala


EDIT: I agree with the sentiment of the down vote, but disagreed with the solution. So I've corrected/broken the code in the question so it has the problem as explained. I've left the answer and my original comment to it. In my opinion, the "def vs val" on line 12 was the non-obvious problem to a beginner such as myself.

I have some Scala that I'm trying to write, but I can't get it to behave exactly the way I want.

What I want to do is call some function, this function should accept some configuration parameters and then configure another function and return that. The caller would then repeatedly use this returned function knowing that it was configured in a certain way. Repeated calls to the returned function should not cause the (expensive) configuration to be re-run.

For example;

1   private def send(host : String, port : Int)(msg : String) = {
2    // send msg to host:port
3  }
4 
5  def sendConfiguredFor(cfg : ConfigOptions) = {
6    // expensive function to figure out what host/port to use
7    val host = ...
8    val port = ...
9
10  send(host, port) _
11 }

The caller should then be able to;

12 def mySend = sendConfiguredFor(someCfg)
13 mySend("msg1")
14 mySend("msg2")

The above is an edited cut/paste from what I have so far. The problem with my implmentation is that every call to "mySend" re-runs the whole of "sendConfigurationFor", which is not what I want.

I'm sure I read a "Scala Puzzler" where this kind of beaviour was the unexpected (and therefore wrong) answer. Now I'm actually trying to use that same behaviour I'm unable to - and I can't find the puzzler either.


Solution

  • First, it's not related to the PartialFunction - don't mess them with partially applied functions.

    Second, it works as expected with val:

    def send(host : String, port : Int)(msg : String) = {
      println("sending...")
    }
    
    def sendConfiguredFor(cfg : Any) = {
    
      val host = "localhost"
      val port = 8080
    
      println("configuring...") //will be called only once
    
      send(host, port) _
    }
    
    scala> val mySend = sendConfiguredFor(null)
    configuring...
    mySend: String => Unit = <function1>
    
    scala> mySend("msg1")
    sending...
    
    scala> mySend("msg2")
    sending...