I'm having some trouble switching from OOP thinking to functional thinking. My current issue is that I have a immutable, persistent data structure which is used (let's say) to build URL-s:
class UrlBuilder {
public UrlBuilder withHost(String domain) {
return new UrlBuilder(/*...*/);
}
public UrlBuilder withPort(Int port) {
return new UrlBuilder(/*...*/);
}
// ...
public String build() {
// ...
}
}
The build()
method which lazily evaluates the string is pretty expensive, so I would like to cache the result.
In OOP this is no problem, because I could just do this:
class UrlBuilder {
private String url;
// ...
public String build() {
if (null == this.url) {
this.url = doExpensiveEvaluation();
}
return this.url;
}
}
and if I needed thread-safety I would just use the doubly-checked locking and be done with it. But to my understanding this is against functional paradigm as it introduces side effect (modifies internal state of the object).
I know that in Scala there is the lazy
keyword which does exactly what I need: implements the so called by-need laziness. But how can I do the same thing in an OOP language? I'm actually very curious how they implement this in Scala.
I tried to inverse the responsibility of caching the result to the consumer of my UrlBuilder
, but this caused the same issue at the consumer side:
class Consumer {
private UrlBuilder urlBuilder;
private String url;
// ...
public String getUrl() {
if (null == this.url) {
this.url = urlBuilder.build(); // same as before!
}
return this.url;
}
}
Hence my question in the title.
EDIT: To be clear: I'm asking about an implementation in OOP language other then Scala. It could be Java or C#, but I also wanted to know how to do this in something like JavaScript. And as I mentioned, I could just use locking, but I was looking for a pure-functional solution without having to use locks.
I was under the impression that functional programming is thread-safe out of the box, thus locking felt to me like an ugly OOP solution. But of course I would also accept an answer which proves that it's not possible. The comment bellow by Ben Reich pretty much says it all: if Scala developers could not do this without locking, then probably I could die trying.