Reputation: 698
I have the following situation:
Upon initialitation (actually first receive) of a socket I want to check something in the handshake (TLS), this has to be only checked upon connection initialization and not on every further receive.
Currently I have an odd:
// this is happening outer scope
var somethingThatGetsComputedinInit = 0
def receive {
if (init) {
somethingThatGetsComputedinInit = doinitstuff(StuffIOnlyGetInitially)
init = false
}
}
Although it would work, this smells so imperative and ugly. What would be a purely functional solution to this?
Upvotes: 4
Views: 2425
Reputation: 2543
You can also consider using var with function callback and update the var to do nothing after the first time:
var firstTimeInit = StuffIOnlyGetInitially => doinitstuff(StuffIOnlyGetInitially)
// this is happening outer scope
var somethingThatGetsComputedinInit = 0
def receive {
somethingThatGetsComputedinInit = firstTimeInit(StuffIOnlyGetInitially)
firstTimeInit = StuffIOnlyGetInitially => ()
}
Upvotes: 0
Reputation: 2913
In your specific example, since you are using actors, you actually can swap out its implementation to model a state machine using "context.become
and context.unbecome
". There is an abstraction layer, Akka FSM, on top of this which provides a nicer syntax for doing exactly this type of thing.
Example partially lifted from the Akka FSM docs:
sealed trait State
case object Initializing extends State
case object Initialized extends State
class Socket extends Actor with FSM[State, Option[Client]] {
startWith(Initializing, None)
when(Initializing) {
case Event(msg: Connect, _) => createClient(msg).fold(stay) {
client =>
//Do more stuff
goto(Initialized) using Some(client)
}
}
when(Initialized) {
case Event(msg: Receive, data@Some(client)) =>
//Do more stuff using client
stay using data
}
initialize()
}
Upvotes: 4
Reputation: 2249
Sounds like good use-case for Scala "lazy val".
lazy val somethingThatGetsComputedinInit = doinitstuff()
You get guarantee that the val gets initialized only once, at the time of first use. I don't know where that is in your code, if it's the proper place, but if not, you could just refer to the val inside "receive" to force the initialization.
Upvotes: 0
Reputation: 16265
This is a case where you want to make use of the lazy val
modifier in scala. This is suggested in Twitter's Effective Scala. Consider the following edits to the example in your question.
class Foo {
def doinitstuff() : Int = {
println("I'm only going to be called once")
42
}
lazy val somethingThatGetsComputedinInit = doinitstuff()
def receive {
println(somethingThatGetsComputedinInit)
}
}
and a client of an instance of Foo calling receive multiple times would output the following:
val foo = new Foo //> foo : worksheet.Foo = worksheet.Foo@5853c95f
foo.receive //> I'm only going to be called once
//| 42
foo.receive //> 42
foo.receive //> 42
Upvotes: 12
Reputation: 3978
Excellent question.
You should definitely read the state pattern.
There is an example, extracted from Eric Gamma Desing patterns book* that works, like you, with TCP connections. It is not functional programming but could serve you as a guide.
*A reference guide for design pattern, hoverer i do not recommend this book, instead i strong encourages you to read Head First : Design pattern that has a more powerful didactic tools for inviting you to thing with design principles, far more important than patterns (and paradimgs).
Hope it helps!
Upvotes: 1