Why Are Option Bool/Int/Double Passed ByRef?

I need to know this for a very specific FSharp compatibility reason. Why do all of the Option types need to be passed by ref?

At some point, the Option I pass in will be replaced by another Option, but when does that happen?

    Rhino.Input.Custom.OptionInteger intOption = new Rhino.Input.Custom.OptionInteger(1, 1, 99);
    Rhino.Input.Custom.OptionDouble dblOption = new Rhino.Input.Custom.OptionDouble(2.2, 0, 99.9);
    Rhino.Input.Custom.OptionToggle boolOption = new Rhino.Input.Custom.OptionToggle(true, "Off", "On");
    string[] listValues = new string[] { "Item0", "Item1", "Item2", "Item3", "Item4" };

Here?

    gp.AddOptionInteger("Integer", ref intOption);
    gp.AddOptionDouble("Double", ref dblOption);
    gp.AddOptionToggle("Boolean", ref boolOption);

Or here?

Rhino.Input.GetResult get_rc = gp.Get();

Classes are always passed as reference. Structs as value. Usually you would only need the ‘ref’ keyword to pass value types by reference. Passing class instances with ref keyword (like the Options classes) seem redundant. However if you design a function you might want to indicate, that you also modify the class instance internal state.

// consider:

HappyRhinoSound FeedRhino(Rhino rhino)
{
      rhino.IsHungry = False; // we modify the state!
      return new HappyRhinoSound();
}
// consumer, who does not know the implementation:
var sound = FeedRhino(myRhino); 
Trace.Assert(!myRhino.StateChanged()); // will fail, because we changed internal state 

#-------------------------

HappyRhinoSound FeedRhino(ref Rhino rhino) // ref acts a indicator that I pass something in and out
{
      rhino.IsHungry = False;
      return new HappyRhinoSound();
}
// consumer:
var sound = FeedRhino(ref myRhino);  // "ok I get a Rhino object in and out" so I would...
Trace.Assert(myRhino.StateChanged());  // ... expect state has changed


#-------------------------

// alternatives:
(HappyRhinoSound , Rhino) FeedRhino(Rhino rhino)
{
      rhino.IsHungry = False;
      return(new HappyRhinoSound(), rhino);
}

Rhino FeedRhino(Rhino rhino, out HappyRhinoSound sound)
{
      rhino.sound = new HappyRhinoSound();
      rhino.IsHungry = False;      
      return rhino;
}

HappyRhinoSound FeedRhino(Rhino rhino, out Rhino modifiedRhino)
{
      modifiedRhino = rhino.Copy(); // optional to copy here.
      modifiedRhino.IsHungry = False;      
      return new HappyRhinoSound();
}

HappyRhinoSound FeedRhinoAndMakeItNotHungry(Rhino rhino)
{
      rhino.IsHungry = False; // we modify the state!
      return new HappyRhinoSound();
}

Heh, weird side effects in stupid OO hierarchies is what drew me to F#. I agree it’s good practice to signal mutation, but I’m used to byref<> as a necessity to copy back structs.

I’m fairly certain RhinoCommon is replacing the options during the call to cmd.AddOption...(...):

HappyRhinoSound FeedRhino(Rhino rhino)
{
      rhino = new Rhino(false); // caller won't see this unless it's passed byref<>
      return new HappyRhinoSound();
}

This threw me and I’ve been struggling to keep my options mutable for the life of the command. Last night I realized it only need be mutable for a single call:

let addOptionsToCmd ( cmd: GetBaseClass) (x: ImmutableType): ImmutableType = 
	let mutable temp = x.OptionInt
	cmd.AddOptionInteger(x.OptionIntLabel, &temp ) |> ignore
	{ x with OptionInt = temp }

cmd.Get() can/will change the state of the x.OptionInt, but cmd.Get() cannot change my reference to x.OptionInt like it did during gp.AddOption...(...).

1 Like