**update 11/17/13:**

Added this example from Wagner book, on the danger of using `HoldAll`. This is a good reason why one should stick to default pass-by-value in Mathematica. Here is the example:

    ClearAll[p, inc, double, x];
    SetAttributes[inc, HoldAll];
    SetAttributes[double, HoldFirst];
    inc[x_] := x = x + 1;
    double[x_] := x + x
    p = 4;
    inc[p];   (* this makes p=5 now *)
    double[p] (* this makes p=10 now *)

gives as expected :

    (*  10  *)

Now changing the call to emulate call by reference, and look what happens now:

    ClearAll[p, inc, double, x];
    SetAttributes[inc, HoldAll];
    SetAttributes[double, HoldFirst];
    inc[x_] := x = x + 1;
    double[x_] := x + x
    p = 4;
    double[inc[p]]  (* what do you think p will be after this? *)

Gives
  
    (* 11 *)

Clearly what happens, as the book says, is that in the second case, `inc[p]` was evaluated twice, and not one time as one would expect.  

Hence  `inc[p]; double[p];` gave different result to `double[inc[p]]`.

**Original answer**

First method: Unevaluated
------------

Wrap the argument you want to pass by reference with `Unevaluated`

    flip[posX_, posY_, lattice_] := 
      Module[{x = posX, y = posY}, 
       lattice = ReplacePart[lattice, {y, x} -> -valueAtPos[x, y, lattice]] //. {{1} :> 
           1, {-1} :> -1}
       ];
    valueAtPos[positionX_, positionY_, lattice_List] := 
     Flatten[Take[lattice, {positionY, positionY}, {positionX, positionX}]]
    s = {{1, 1, 1, -1}, {1, 1, -1, -1}, {-1, -1, 1, -1}, {-1, 1, -1, 1}};

Now

    flip[1, 1, Unevaluated[s]];
    s
    (*  {{-1, 1, 1, -1}, {1, 1, -1, -1}, {-1, -1, 1, -1}, {-1, 1, -1, 1}}  *)

Disadvantages: You lose the ability to add type checking (as in adding `lattice_List` in the formal parameter, must write it as `lattice` only )

Second method: HoldAll
-------------

Make the function itself HoldAll

    ClearAll[flip];
    flip[posX_, posY_, lattice_] := 
      Module[{x = posX, y = posY}, 
       lattice = 
        ReplacePart[lattice, {y, x} -> -valueAtPos[x, y, lattice]] //. {{1} :> 
           1, {-1} :> -1}
       ];
    Attributes[flip] = HoldAll;
    valueAtPos[positionX_, positionY_, lattice_List] := 
     Flatten[Take[lattice, {positionY, positionY}, {positionX, positionX}]]
    s = {{1, 1, 1, -1}, {1, 1, -1, -1}, {-1, -1, 1, -1}, {-1, 1, -1, 1}};

now

    flip[1, 1, s];
    s
    (* {{-1, 1, 1, -1}, {1, 1, -1, -1}, {-1, -1, 1, -1}, {-1, 1, -1, 1}} *)

Third method  HoldFirst
------------

Move the one argument to be passed by reference to first argument and use `HoldFirst`

    ClearAll[flip];
    flip[lattice_, posX_, posY_] := 
      Module[{x = posX, y = posY}, 
       lattice = 
        ReplacePart[
          lattice, {y, x} -> -valueAtPos[x, y, lattice]] //. {{1} :> 
           1, {-1} :> -1}
       ];
    Attributes[flip] = HoldFirst;
    valueAtPos[positionX_, positionY_, lattice_List] := 
     Flatten[Take[lattice, {positionY, positionY}, {positionX, positionX}]]
    s = {{1, 1, 1, -1}, {1, 1, -1, -1}, {-1, -1, 1, -1}, {-1, 1, -1, 1}};

gives

    flip[s, 1, 1];
    s
    (* {{-1, 1, 1, -1}, {1, 1, -1, -1}, {-1, -1, 1, -1}, {-1, 1, -1, 1}} *)

But I think, in the spirit of Mathematica programming and functional programming, is that one should not do these things. i.e. functions should not have side-effects. To modify something, write `newValue = foo[ oldValue ]` and pass things by value, which is the default.

see also [passing-large-list-by-reference][1] and  [does-pass-by-value-affect-the-performance-of-function-calls][2]


  [1]: http://mathematica.stackexchange.com/questions/19445/passing-large-list-by-reference
  [2]: http://mathematica.stackexchange.com/questions/20865/does-pass-by-value-affect-the-performance-of-function-calls