Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

In an interview with John Hughes where he talks about Erlang and Haskell, he has the following to say about using stateful libraries in Erlang:

If I want to use a stateful library, I usually build a side effect-free interface on top of it so that I can then use it safely in the rest of my code.

What does he mean by this? I am trying to think of an example of how this would look, but my imagination and/or knowledge is failing me.

share|improve this question
    
Well, if the state exists, it won't go away. The trick is to create something that will keep track of the dependency. The standard Haskell answer is "monads" or the more advanced "arrows". They are a bit hard to wrap your head around and I never really did, so somebody else would have to try explaining them. –  Jan Hudec Nov 8 '13 at 7:52

2 Answers 2

up vote 11 down vote accepted

(I don't know Erlang, and I can't write Haskell, but I think I can answer nevertheless)

Well, in that interview the example of a random number generation library is given. Here is a possible stateful interface:

# create a new RNG
var rng = RNG(seed)

# every time we call the next(ceil) method, we get a new random number
print rng.next(10)
print rng.next(10)
print rng.next(10)

Output may be 5 2 7. To someone who likes immutability, this is plain wrong! It should be 5 5 5, because we called the method on the same object.

So what would a stateless interface be? We can view the sequence of random numbers as a lazily evaluated list, where next actually retrieves the head:

let rng = RNG(seed)
let n : rng = rng in
  print n
  let n : rng = rng in
    print n
    let n : rng in
      print n

With such an interface, we can always revert to a previous state. If two pieces of your code refer to the same RNG, they will actually get the same sequence of numbers. In a functional mindset, this is highly desirable.

Implementing this in a stateful language isn't that complicated. For example:

import scala.util.Random
import scala.collection.immutable.LinearSeq

class StatelessRNG (private val statefulRNG: Random, bound: Int) extends LinearSeq[Int] {
  private lazy val next = (statefulRNG.nextInt(bound), new StatelessRNG(statefulRNG, bound))

  // the rest is just there to satisfy the LinearSeq trait
  override def head = next._1
  override def tail = next._2
  override def isEmpty = false
  override def apply(i: Int): Int = throw new UnsupportedOperationException()
  override def length = throw new UnsupportedOperationException()
}

// print out three nums
val rng = new StatelessRNG(new Random(), 10)
rng.take(3) foreach (n => println(n))

Once you add a bit of syntactic sugar so that it feels like a list, this is actually quite nice.

share|improve this answer
1  
As for your first example: rnd.next(10) producing different values every time does not have to do with immutability as much as it has to do with the definition of a function: functions must be 1-to-1. (+1 though, good stuff) –  Steve Evers Nov 7 '13 at 18:38
    
Thanks! That was a really nice, perspicuous explanation and example. –  beta Nov 8 '13 at 12:17

A key concept here is that of external mutable state. A library that has no external mutable state, is one that is free of side-effects. Every function in such a library only depends on the arguments passed into it.

  • If your function accesses any resource that wasn't created by it, given to it (ie. as a parameter), then it depends on external state.
  • If your function creates something that it doesn't pass back to the caller (and doesn't destroy it) then your function is creating external state.
  • When the external state from above can have different values at different times, then it is mutable.

Handy litmus tests that I use:

  • if function A needs to be run before function B then A creates external state that B depends on.
  • if a function I'm writing cannot be memoized, then it depends on external mutable state. (The memoization might not be a good idea because of memory pressure, but it should still be possible)
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.