Word splitting / text parsing with consideration for quotes Is the MoveNext operation inefficient here?  I really just want to increment an index.  Originally, I had a mutable variable locally in ExtractTokens to accommodate this.  I would have to believe that a mutable variable there would have been pretty efficient, but I'm trying hard not to have them.  I can't help but feel that creating a new Record to increment an integer is bad somehow.  I guess I'm wondering what a real-world F# developer would have chosen to do here.

This starts in ExtractTokens.  It basically iterates each character in a string.  On spaces it yields the previous word, unless in a quote either single or double.

type Quote = 
   | Single
   | Double
   | None

type State = {
   Start : int
   Index : int
   Quote : Quote
   }

let InitState : State = 
   {Start = 0; Index = 0; Quote = None }

let MoveNext (s : State) : State = 
   {s with Index = s.Index + 1}

let FlipQuote (s : State) (text : string) : State = 
   match text.[s.Index], s.Quote with
   | '\'', None   -&gt; {s with Quote = Single}
   | '"' , None   -&gt; {s with Quote = Double} 
   | '\'', Single -&gt; {s with Quote = None}
   | '"' , Double -&gt; {s with Quote = None}
   | _ -&gt; s

let StartNew (s : State) : State = 
   {Start = s.Index; Index = s.Index; Quote = None}

let NotEmpty (s : string) : bool =
   not (System.String.IsNullOrWhiteSpace(s))

let GetToken (s : State) (text : string) : string =
   text.Substring(s.Start, s.Index - s.Start)

let GetChar (s : State) (text : string) : string =
   text.[s.Index].ToString()

let ExtractTokens (text : string) : seq&lt;string&gt; = 
   let rec loop state = 
      seq {
         if (state.Index &lt; text.Length) then
            match text.[state.Index], state.Quote with
            | '"' , _  
            | '\'', _     -&gt; yield! FlipQuote state text |&gt; MoveNext |&gt; loop
            | ' ' , None   
            | '(' , None
            | ')' , None  -&gt; yield  GetToken state text
                             yield  GetChar  state text
                             yield! MoveNext state |&gt; StartNew |&gt; loop 
            | _   , _     -&gt; yield! MoveNext state |&gt; loop
         else
            yield GetToken state text
      }
   loop InitState |&gt; Seq.where NotEmpty


Refactored

type Quote = 
   | Single
   | Double
   | None

type State = {
   Start : int
   Quote : Quote
   }

let InitState : State = 
   {Start = 0; Quote = None }

let FlipQuote (state : State) (index : int) (text : string) : State = 
   match text.[index], state.Quote with
   | '\'', None   -&gt; {state with Quote = Single}
   | '"' , None   -&gt; {state with Quote = Double} 
   | '\'', Single
   | '"' , Double -&gt; {state with Quote = None}
   | _ -&gt; state

let StartNew (state : State) (index : int) : State = 
   {Start = index; Quote = None}

let NotEmpty = 
   System.String.IsNullOrWhiteSpace &gt;&gt; not   

let GetToken (state : State) (index : int) (text : string) : string =
   text.Substring(state.Start, index - state.Start)

let GetChar (index : int) (text : string) : string =
   text.[index].ToString()

let ExtractTokens (text : string) : seq&lt;string&gt; = 
   let rec loop index state = 
      seq {
         if (index &lt; text.Length) then
            match text.[index], state.Quote with
            | '"' , _  
            | '\'', _     -&gt; yield! FlipQuote state index text |&gt; loop (index + 1)
            | ' ' , None   
            | '(' , None
            | ')' , None  -&gt; yield  GetToken state index text
                             yield  GetChar  index text
                             yield! StartNew state (index + 1) |&gt; loop  (index + 1)
            | _   , _     -&gt; yield! state |&gt; loop (index + 1)
         else
            yield GetToken state index text
      }
   loop 0 InitState |&gt; Seq.where NotEmpty
I'm not saying the below revision is an improvement but it has eliminated the need for MoveNext. The penalty is to have the index as a separate argument to loop which violates the idea of a state object. One could argue that the index is not a part of the (splitting)state but just an iterator variable and then everything are nicely justified again :-)

module RevisedSplitting =

    type String with
        member this.charAt index =
            this.[index]

    type Quote = 
       | Single
       | Double
       | None

    type State = {
       Start : int
       Quote : Quote
       }

    let InitState = 
       {Start = 0; Quote = None }

    let FlipQuote s index (text : string) = 
       match text.charAt index, s.Quote with
       | '\'', None   -&gt; {s with Quote = Single}
       | '"' , None   -&gt; {s with Quote = Double} 
       | '\'', Single -&gt; {s with Quote = None}
       | '"' , Double -&gt; {s with Quote = None}
       | _ -&gt; s

    let StartNew s index = 
       {Start = index; Quote = None}

    let NotEmpty s =
       not (System.String.IsNullOrWhiteSpace(s))

    let GetToken s index text =
       (string text).Substring(s.Start, index - s.Start)

    let GetChar index (text : string) =
       sprintf "%c" (text.charAt index)

    let ExtractTokens (text : string) = 
       let rec loop index state = 
          seq {
             if index &lt; (text |&gt; Seq.length) then
                match text.charAt index, state.Quote with
                | '"' , _  
                | '\'', _     -&gt; yield! FlipQuote state index text |&gt; loop (index + 1)
                | ' ' , None   
                | '(' , None
                | ')' , None  -&gt; yield  GetToken state index text
                                 yield  GetChar  index text
                                 yield! StartNew state (index + 1) |&gt; loop (index + 1)  
                | _   , _     -&gt; yield! state |&gt; loop (index + 1)
             else
                yield GetToken state index text
          }
       loop 0 InitState |&gt; Seq.where NotEmpty