1

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.

Community
  • 1
  • 1
Maciej Sz
  • 11,151
  • 7
  • 40
  • 56
  • 3
    Modifying internal state that cannot be observed is not wrong in functional programming. – Bergi Jul 06 '15 at 13:24
  • 1
    I'm confused as to why `lazy` doesn't work for you here. Can you explain why it doesn't suffice? Are you asking how to implement `lazy` in another language, like Java? I would also be careful with some of your language here: object oriented and functional programming are not at odds with each other – Scala supports both paradigms! – Ben Reich Jul 06 '15 at 13:56
  • 3
    You can view the decompiled code when using `lazy` to better understand the implementation. Read more about that here: http://stackoverflow.com/a/17642466/1223622 – Ben Reich Jul 06 '15 at 14:01
  • @Bergi You could be right here. If only there was a way to do this with out-of-the-box thread-safety. Can you point to some references to back up your claim? – Maciej Sz Jul 06 '15 at 14:05
  • @MaciejSz: Hm, maybe "if it doesn't change anything obervable, then it's not a [side effect](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) by definition"? – Bergi Jul 06 '15 at 14:10
  • @BenReich That is exactly what I'm asking: implementation in OOP language other then Scala. And judging by the link you gave it seems that Scala also uses locking to implement this. I was actually hoping to find a clean-functional solution without locking. But I would also accept an answer which proves that it's not possible. – Maciej Sz Jul 06 '15 at 14:10

3 Answers3

1

We're talking java aren't we? Why not just synchronize it?

class LazyClass 
{

    Integer someValue = null;
    public synchronized Integer someReallyExpensiveMethod() {
        if (someValue == null)
        {
            someValue = 1 + 2 + 3; // .. + 32 + .. this takes a long time
        }
        return someValue;
    }

}
Derrops
  • 7,651
  • 5
  • 30
  • 60
  • I vote this up, because this is probably the only solution, which was mentioned earlier in [this comment](http://stackoverflow.com/questions/31246650/how-to-implement-the-by-need-lazy-evaluation-in-an-oop-language-so-that-it-comp/31258128#comment50492980_31246650) by Ben Reich. Of course I would use double-checked locking here instead of just synchronizing the method. – Maciej Sz Jul 07 '15 at 09:39
  • Or perhaps not syncronize the accessor in case someValue already is generated. Wrapper for someRXM still needs the check to prevent races though. – Sylwester Jul 07 '15 at 11:41
  • No need for volatile or wrapping this will suffice. Only one thread can access this method at a time. Only if other methods are gaining access to someValue will you have weird behavior. – Derrops Jul 08 '15 at 01:01
0

How about this:

object UrlBuilder{
    def empty = new InnerBuilder("")

    class InnerBuilder(...){
        def withHost(host: String) = new InnerBuilder(...)
        def withPort(port: Int) = new InnerBuilder(...)
        def build(): String = ...
    }

that way you don't have any mutable states }

and use it like:

UrlBuilder.empty
          .withHost(...)
          .withPort(...)
          .build()
lev
  • 3,986
  • 4
  • 33
  • 46
  • That doesn't change the fact that the following code will execute `build` twice, and not cache the value: `val url = UrlBuilder.empty.withHost(...).withPort(...); url.build(); url.build()` – Kaarel Nummert Jul 06 '15 at 16:33
  • The implementation of the `build()` method is the most important part, and you omitted it. – Maciej Sz Jul 07 '15 at 09:32
  • it could be simply a lazy val, and everything will stay immutable, and will be computed only once – lev Jul 07 '15 at 10:01
  • The question was actually how to implement the lazy val. – Maciej Sz Jul 07 '15 at 10:23
0

I've found the best answer to this question in this article by Rich Hickey. It's about a Closure implementation of so called transient data structures. They essentially operate on mutable copy of a persistent data structure, but do this transparently for the outside world (using locking in the back stage).

Aside of describing how the data structure works the article essentially states that it is OK to have mutation as long as the mutation cannot be observed.

As it turns out this is a somehow philosophical issue. The quote from the article summarizes this pretty good:

If a tree falls in the woods, does it make a sound?

If a pure function mutates some local data in order to produce an immutable return value, is that ok?

— Rich Hickey, Clojure

Maciej Sz
  • 11,151
  • 7
  • 40
  • 56