<div dir="ltr">Hi, all,<div><br></div><div>Here's my question:</div><div><br></div><div><p style="color:rgb(0,0,0);font-family:"segoe ui";font-size:medium">I thought, for grins, I'd try to turn some log output into a dependency graph (using GraphViz's <code>dot(1)</code>). I'm having difficulty forcing my stateful paradigm into a functional one, so I need some help.</p><p style="color:rgb(0,0,0);font-family:"segoe ui";font-size:medium">If I was to do this with an imperative (stateful) language, I'd build a set of edges (or a map to a frequency count, really, since I'll use <code>freq > 1</code> to add some output text noting the repeated occurrences), and then dump out the set elements to a text file that would look something like this fragment:</p><pre class="example" style="border:1px solid rgb(204,204,204);padding:8pt;overflow:auto;margin:1.2em;background-color:rgb(243,245,247);color:rgb(0,0,0)">a -> q
q -> d
d -> e [color=red]
d -> f [color=red
</pre><p style="color:rgb(0,0,0);font-family:"segoe ui";font-size:medium">My big problem now is that if I process a subtree that looks like:</p><pre class="example" style="border:1px solid rgb(204,204,204);padding:8pt;overflow:auto;margin:1.2em;background-color:rgb(243,245,247);color:rgb(0,0,0)">a
  b
    c
    d
  b
    d
    e
</pre><p style="color:rgb(0,0,0);font-family:"segoe ui";font-size:medium">my current plan is to proces the first b-c-d subtree and then process the b-d-e subtree, <i>BUT</i> I need to pass the updated edge set to the second processing call, which is pretty stateful.</p><p style="color:rgb(0,0,0);font-family:"segoe ui";font-size:medium">Do I need to just bite the bullet and find some succinct way to do that, or is my entire approach just wrong, stuck in my stateful mindset?</p><div id="gmail-content" style="color:rgb(0,0,0);font-family:"segoe ui";font-size:medium"><div id="gmail-outline-container-orgheadline3" class="gmail-outline-2"><div class="gmail-outline-text-2" id="gmail-text-3"><p>My (awful) code looks like this:</p><div class="gmail-org-src-container"><pre class="gmail-src gmail-src-haskell" style="border:1px solid rgb(204,204,204);padding:1.2em 8pt 8pt;overflow:auto;margin:1.2em;background-color:rgb(243,245,247)"><span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">Emit to stdout a series of dot(1) edges specifying dependencies.</span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">"A -> B" means "A depends on B".</span>
<span style="color:rgb(54,100,139);font-style:italic">--</span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">Build with 'ghc dependency-graph.hs'</span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">Input is a text file containing lines as follows:</span>
<span style="color:rgb(54,100,139);font-style:italic">--      </span><span style="color:rgb(54,100,139);font-style:italic">(some indentation) (some extraneous text) (file-A) in (some directory)</span>
<span style="color:rgb(54,100,139);font-style:italic">--          </span><span style="color:rgb(54,100,139);font-style:italic">(some extra indentation) (some extraneous text) (file-B) in (some directory)</span>
<span style="color:rgb(54,100,139);font-style:italic">--      </span><span style="color:rgb(54,100,139);font-style:italic">(some indentation matching the first line above) (some extraneous text) (file-C) in (some directory)</span>
<span style="color:rgb(54,100,139);font-style:italic">--</span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">This means that file-A depends on file-B, but neither file-A nor file-B depend on file-C.</span>
<span style="color:rgb(54,100,139);font-style:italic">--</span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">Sample:</span>
<span style="color:rgb(54,100,139);font-style:italic">--    </span><span style="color:rgb(54,100,139);font-style:italic">Helios.MigrationTool.Common.AssemblyUtils.GetAssemblyList() Information: 0 : Processing SXA.Compass.Config.ViewModel.dll  in C:\Program Files (x86)\Allscripts Sunrise\Clinical Manager Client\7.2.5575.0\</span>
<span style="color:rgb(54,100,139);font-style:italic">--    </span><span style="color:rgb(54,100,139);font-style:italic">Helios.MigrationTool.Common.AssemblyUtils.GetAssemblyList() Information: 0 : Adding C:\Program Files (x86)\Allscripts Sunrise\Clinical Manager Client\7.2.5575.0\SXA.Compass.Config.ViewModel.dll (IsPresent=true)        to assemblyList at beginning of GetAssemblyListEx()</span>
<span style="color:rgb(54,100,139);font-style:italic">--      </span><span style="color:rgb(54,100,139);font-style:italic">Helios.MigrationTool.Common.AssemblyUtils.GetAssemblyList() Information: 0 : Processing SXA.Compass.Config.Utils.dll    in C:\Program Files (x86)\Allscripts Sunrise\Clinical Manager Client\7.2.5575.0\</span>
<span style="color:rgb(54,100,139);font-style:italic">--</span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">(Need to skip the line containing "Adding", and only process the ones containing "Processing".)</span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">Algorithm:</span>
<span style="color:rgb(54,100,139);font-style:italic">--      </span><span style="color:rgb(54,100,139);font-style:italic">Read first line, parse, remember indentation</span>
<span style="color:rgb(54,100,139);font-style:italic">--      </span><span style="color:rgb(54,100,139);font-style:italic">Repeat for other lines, but if indentation increases, store pair A -> B in hashset.</span>
<span style="color:rgb(54,100,139);font-style:italic">--      </span><span style="color:rgb(54,100,139);font-style:italic">At end, dump out hashset.</span>

<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">import Debug.Trace</span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">import System.Environment</span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">import System.Console.GetOpt</span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">import Data.Maybe (fromMaybe)</span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">import Data.List.Split</span>
<span style="color:rgb(160,32,240)">import</span> <span style="color:rgb(34,139,34)">Prelude</span> <span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">hiding (readFile) -- Because we want the System.IO.Strict version</span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">import System.IO (hPutStr, hPutStrLn, stderr)</span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">import System.IO.Strict</span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">import Control.Monad</span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">import System.Directory</span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">import System.FilePath</span>
<span style="color:rgb(160,32,240)">import</span> <span style="color:rgb(34,139,34)">Text.Regex.TDFA</span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">import Text.Regex.TDFA.String</span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">import Text.Printf</span>

<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">import qualified Data.Map.Lazy as Map</span>
<span style="color:rgb(160,32,240)">import</span> <span style="color:rgb(160,32,240)">qualified</span> <span style="color:rgb(34,139,34)">Data.Map.Strict</span> <span style="color:rgb(160,32,240)">as</span> <span style="color:rgb(34,139,34)">Map</span>

<span style="color:rgb(54,100,139);font-style:italic">--</span><span style="color:rgb(54,100,139);font-style:italic">-------------------------------------------------------------- Test Data</span>
<span style="color:rgb(0,0,255)">l1</span> <span style="color:rgb(139,105,20)">=</span> <span style="color:rgb(178,34,34)">"    Helios.MigrationTool.Common.AssemblyUtils.GetAssemblyList() Information: 0 : Processing SXA.Compass.Config.ViewModel.dll\tin C:\\Program Files (x86)\\Allscripts Sunrise\\Clinical Manager Client\\7.2.5575.0\\"</span>
<span style="color:rgb(0,0,255)">l2</span> <span style="color:rgb(139,105,20)">=</span> <span style="color:rgb(178,34,34)">"    Helios.MigrationTool.Common.AssemblyUtils.GetAssemblyList() Information: 0 : Adding C:\\Program Files (x86)\\Allscripts Sunrise\\Clinical Manager Client\\7.2.5575.0\\SXA.Compass.Config.ViewModel.dll\t(IsPresent=true)\tto assemblyList at beginning of GetAssemblyListEx()"</span>
<span style="color:rgb(0,0,255)">l3</span> <span style="color:rgb(139,105,20)">=</span> <span style="color:rgb(178,34,34)">"      Helios.MigrationTool.Common.AssemblyUtils.GetAssemblyList() Information: 0 : Processing SXA.Compass.Config.Utils.dll\tin C:\\Program Files (x86)\\Allscripts Sunrise\\Clinical Manager Client\\7.2.5575.0\\"</span>
<span style="color:rgb(54,100,139);font-style:italic">--</span><span style="color:rgb(54,100,139);font-style:italic">-------------------------------------------------------------- Test Data Ends</span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">See <a href="http://stackoverflow.com/q/32149354/370611">http://stackoverflow.com/q/32149354/370611</a></span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">toRegex = makeRegexOpts defaultCompOpt{multiline=False} defaultExecOpt</span>

<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">Escape parens?</span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">initialFillerRegex :: String</span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">initialFillerRegex = "Helios.MigrationTool.Common.AssemblyUtils.GetAssemblyList\\(\\) Information: 0 : Processing"</span>

<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">Regex matching (marking) a line to be processed</span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">valuableLineRegex :: String</span>
<span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">valuableLineRegex = "\\bProcessing\\b"</span>

<span style="color:rgb(72,61,139);font-style:italic">-- |Regex matching line to be parsed</span>
<span style="color:rgb(0,0,255)">parseLineRegex</span> <span style="color:rgb(139,105,20)">::</span> <span style="color:rgb(34,139,34)">String</span>
<span style="color:rgb(0,0,255)">parseLineRegex</span> <span style="color:rgb(139,105,20)">=</span> <span style="color:rgb(178,34,34)">"^(.* Information: 0 : Processing )([^ ]*)[ \t]+in (.*)"</span> <span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">3subexpressions</span>

<span style="color:rgb(0,0,255)">main</span> <span style="color:rgb(139,105,20)">::</span> <span style="color:rgb(34,139,34)">IO</span><span style="color:rgb(34,139,34)">()</span>
<span style="color:rgb(0,0,255)">main</span> <span style="color:rgb(139,105,20)">=</span> <span style="color:rgb(160,32,240)">do</span>
  logContents <span style="color:rgb(139,105,20)"><-</span> getContents
  putStrLn <span style="color:rgb(139,105,20)">$</span> unlines <span style="color:rgb(139,105,20)">$</span> fst <span style="color:rgb(139,105,20)">$</span> edges (parseIndent <span style="color:rgb(139,105,20)">$</span> lines logContents) Map.empty

<span style="color:rgb(54,100,139);font-style:italic">--</span><span style="color:rgb(54,100,139);font-style:italic">--------------------------------------------------------------</span>
<span style="color:rgb(72,61,139);font-style:italic">-- |Parses out the leading indentation of the given String into a string of spaces and the rest of the line</span>
<span style="color:rgb(0,0,255)">parseIndent</span> <span style="color:rgb(139,105,20)">::</span> <span style="color:rgb(34,139,34)">String</span> <span style="color:rgb(139,105,20)">-></span> (<span style="color:rgb(34,139,34)">String</span>,<span style="color:rgb(34,139,34)">String</span>)
<span style="color:rgb(0,0,255)">parseIndent</span> s <span style="color:rgb(139,105,20)">=</span> ((fourth <span style="color:rgb(139,105,20)">$</span> (s <span style="color:rgb(139,105,20)">=~</span> <span style="color:rgb(178,34,34)">"^( *)(.*)"</span> <span style="color:rgb(139,105,20)">::</span> (<span style="color:rgb(34,139,34)">String</span>,<span style="color:rgb(34,139,34)">String</span>,<span style="color:rgb(34,139,34)">String</span>,[<span style="color:rgb(34,139,34)">String</span>]))) <span style="color:rgb(139,105,20)">!!</span> 0,
                 (fourth <span style="color:rgb(139,105,20)">$</span> (s <span style="color:rgb(139,105,20)">=~</span> <span style="color:rgb(178,34,34)">"^( *)(.*)"</span> <span style="color:rgb(139,105,20)">::</span> (<span style="color:rgb(34,139,34)">String</span>,<span style="color:rgb(34,139,34)">String</span>,<span style="color:rgb(34,139,34)">String</span>,[<span style="color:rgb(34,139,34)">String</span>]))) <span style="color:rgb(139,105,20)">!!</span> 1)

<span style="color:rgb(54,100,139);font-style:italic">--</span><span style="color:rgb(54,100,139);font-style:italic">--------------------------------------------------------------</span>
<span style="color:rgb(72,61,139);font-style:italic">-- |Returns a list of strings describing edges in the form "a -> b </span><span style="color:rgb(72,61,139);font-style:italic">/* comment */</span><span style="color:rgb(72,61,139);font-style:italic">"</span>
<span style="color:rgb(0,0,255)">edges</span> <span style="color:rgb(139,105,20)">::</span> 
  [(<span style="color:rgb(34,139,34)">String</span>,<span style="color:rgb(34,139,34)">String</span>)]             <span style="color:rgb(72,61,139);font-style:italic">-- ^ Input tuples: (indent, restOfString)</span>
  <span style="color:rgb(139,105,20)">-></span> <span style="color:rgb(34,139,34)">Map.Map</span> <span style="color:rgb(34,139,34)">String</span> <span style="color:rgb(34,139,34)">Int</span> <span style="color:rgb(72,61,139);font-style:italic">-- ^ Map of edges in form "a -> b" with a count of the number of times that edge occurs</span>
  <span style="color:rgb(139,105,20)">-></span> [<span style="color:rgb(34,139,34)">String</span>]           <span style="color:rgb(72,61,139);font-style:italic">-- ^ Output list of edge descriptions in form "a -> b optionalExtraText"</span>

<span style="color:rgb(0,0,255)">edges</span> <span style="color:rgb(34,139,34)">[]</span> edgeSet <span style="color:rgb(139,105,20)">=</span>
  (edgeDump <span style="color:rgb(139,105,20)">$</span> Map.assocs edgeSet, 0)

<span style="color:rgb(0,0,255)">edges</span> (lastLine<span style="color:rgb(34,139,34)">:[]</span>) edgeSet <span style="color:rgb(139,105,20)">=</span>
  (edgeDump <span style="color:rgb(139,105,20)">$</span> Map.assocs edgeSet, 1)

<span style="color:rgb(0,0,255)">edges</span> (fstLogLine<span style="color:rgb(34,139,34)">:</span>sndLogLine<span style="color:rgb(34,139,34)">:[]</span>) edgeSet <span style="color:rgb(139,105,20)">=</span>
  <span style="color:rgb(160,32,240)">let</span> fstFields <span style="color:rgb(139,105,20)">=</span> (snd fstLogLine) <span style="color:rgb(139,105,20)">=~</span> parseLineRegex <span style="color:rgb(139,105,20)">::</span> (<span style="color:rgb(34,139,34)">String</span>,<span style="color:rgb(34,139,34)">String</span>,<span style="color:rgb(34,139,34)">String</span>,[<span style="color:rgb(34,139,34)">String</span>])
      sndFields <span style="color:rgb(139,105,20)">=</span> (snd sndLogLine) <span style="color:rgb(139,105,20)">=~</span> parseLineRegex <span style="color:rgb(139,105,20)">::</span> (<span style="color:rgb(34,139,34)">String</span>,<span style="color:rgb(34,139,34)">String</span>,<span style="color:rgb(34,139,34)">String</span>,[<span style="color:rgb(34,139,34)">String</span>])
  <span style="color:rgb(160,32,240)">in</span>
    <span style="color:rgb(160,32,240)">if</span> length (fourth fstFields) <span style="color:rgb(139,105,20)">==</span> 0
    <span style="color:rgb(160,32,240)">then</span> error (<span style="color:rgb(178,34,34)">"Unmatched: "</span> <span style="color:rgb(139,105,20)">++</span> (first fstFields)) <span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">First line must always match</span>
    <span style="color:rgb(160,32,240)">else</span> <span style="color:rgb(160,32,240)">if</span> length (fourth sndFields) <span style="color:rgb(139,105,20)">==</span> 0 <span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">"Adding", not "Processing"</span>
    <span style="color:rgb(160,32,240)">then</span> edges (fstLogLine<span style="color:rgb(34,139,34)">:[]</span>) edgeSet <span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">Skip useless line</span>
    <span style="color:rgb(160,32,240)">else</span> <span style="color:rgb(160,32,240)">if</span> indentLength fstLogLine <span style="color:rgb(139,105,20)">>=</span> indentLength sndLogLine
    <span style="color:rgb(160,32,240)">then</span> edges (sndLogLine<span style="color:rgb(34,139,34)">:[]</span>) edgeSet <span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">Can't be an edge from first to second line; drop first line and keep going.</span>
    <span style="color:rgb(160,32,240)">else</span> edges (sndLogLine<span style="color:rgb(34,139,34)">:[]</span>)
         (Map.insertWith (<span style="color:rgb(139,105,20)">+</span>) ((fullName fstFields) <span style="color:rgb(139,105,20)">++</span> (fullName sndFields)) 1)

<span style="color:rgb(0,0,255)">edges</span> (fstLogLine<span style="color:rgb(34,139,34)">:</span>sndLogLine<span style="color:rgb(34,139,34)">:</span>thdLogLine<span style="color:rgb(34,139,34)">:</span>logLines) edgeSet <span style="color:rgb(139,105,20)">=</span>
  <span style="color:rgb(160,32,240)">let</span> fstFields <span style="color:rgb(139,105,20)">=</span> (snd fstLogLine) <span style="color:rgb(139,105,20)">=~</span> parseLineRegex <span style="color:rgb(139,105,20)">::</span> (<span style="color:rgb(34,139,34)">String</span>,<span style="color:rgb(34,139,34)">String</span>,<span style="color:rgb(34,139,34)">String</span>,[<span style="color:rgb(34,139,34)">String</span>])
      sndFields <span style="color:rgb(139,105,20)">=</span> (snd sndLogLine) <span style="color:rgb(139,105,20)">=~</span> parseLineRegex <span style="color:rgb(139,105,20)">::</span> (<span style="color:rgb(34,139,34)">String</span>,<span style="color:rgb(34,139,34)">String</span>,<span style="color:rgb(34,139,34)">String</span>,[<span style="color:rgb(34,139,34)">String</span>])
      thdFields <span style="color:rgb(139,105,20)">=</span> (snd thdLogLine) <span style="color:rgb(139,105,20)">=~</span> parseLineRegex <span style="color:rgb(139,105,20)">::</span> (<span style="color:rgb(34,139,34)">String</span>,<span style="color:rgb(34,139,34)">String</span>,<span style="color:rgb(34,139,34)">String</span>,[<span style="color:rgb(34,139,34)">String</span>])
  <span style="color:rgb(160,32,240)">in</span>
    <span style="color:rgb(160,32,240)">if</span> length (fourth fstFields) <span style="color:rgb(139,105,20)">==</span> 0
    <span style="color:rgb(160,32,240)">then</span> error (<span style="color:rgb(178,34,34)">"Unmatched: "</span> <span style="color:rgb(139,105,20)">++</span> (first fstFields)) <span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">First line must always match</span>

    <span style="color:rgb(160,32,240)">else</span> <span style="color:rgb(160,32,240)">if</span> length (fourth sndFields) <span style="color:rgb(139,105,20)">==</span> 0 <span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">"Adding", not "Processing"</span>
    <span style="color:rgb(160,32,240)">then</span> edges (fstLogLine<span style="color:rgb(34,139,34)">:</span>thdLogLine<span style="color:rgb(34,139,34)">:</span>logLines) edgeSet <span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">Skip useless line</span>

    <span style="color:rgb(160,32,240)">else</span> <span style="color:rgb(160,32,240)">if</span> indentLength fstLogLine <span style="color:rgb(139,105,20)">>=</span> indentLength sndLogLine
    <span style="color:rgb(160,32,240)">then</span> <span style="color:rgb(34,139,34)">[]</span>                     <span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">Stop processing at outdent</span>

    <span style="color:rgb(160,32,240)">else</span>
      <span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">Looking one of:</span>
      <span style="color:rgb(54,100,139);font-style:italic">--       </span><span style="color:rgb(54,100,139);font-style:italic">1</span>
      <span style="color:rgb(54,100,139);font-style:italic">--          </span><span style="color:rgb(54,100,139);font-style:italic">2 -- process 1 -> 2, then process 2.. as subtree</span>
      <span style="color:rgb(54,100,139);font-style:italic">--             </span><span style="color:rgb(54,100,139);font-style:italic">3 -- Need to process as subtree rooted at 2, then drop subtree (zero or more lines at same level as 3)</span>
      <span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">or</span>
      <span style="color:rgb(54,100,139);font-style:italic">--       </span><span style="color:rgb(54,100,139);font-style:italic">1</span>
      <span style="color:rgb(54,100,139);font-style:italic">--          </span><span style="color:rgb(54,100,139);font-style:italic">2 -- processs, then drop this line (process 2.. as empty subtree?)</span>
      <span style="color:rgb(54,100,139);font-style:italic">--          </span><span style="color:rgb(54,100,139);font-style:italic">3</span>
      <span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">or</span>
      <span style="color:rgb(54,100,139);font-style:italic">--       </span><span style="color:rgb(54,100,139);font-style:italic">1</span>
      <span style="color:rgb(54,100,139);font-style:italic">--          </span><span style="color:rgb(54,100,139);font-style:italic">2 -- process, then drop this line (drop entire subtree rooted at 1) (same as above, drop empty subtree? (2))</span>
      <span style="color:rgb(54,100,139);font-style:italic">--       </span><span style="color:rgb(54,100,139);font-style:italic">3</span>
      <span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">or</span>
      <span style="color:rgb(54,100,139);font-style:italic">--       </span><span style="color:rgb(54,100,139);font-style:italic">1</span>
      <span style="color:rgb(54,100,139);font-style:italic">--          </span><span style="color:rgb(54,100,139);font-style:italic">2 -- same as above? Drop empty subtree rooted at 2</span>
      <span style="color:rgb(54,100,139);font-style:italic">--    </span><span style="color:rgb(54,100,139);font-style:italic">3</span>
      edges (sndLogLine<span style="color:rgb(34,139,34)">:</span>thdLogLine<span style="color:rgb(34,139,34)">:</span>logLines) (Map.insertWith (<span style="color:rgb(139,105,20)">+</span>) ((fullName fstFields) <span style="color:rgb(139,105,20)">++</span> (fullName sndFields)) 1) <span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">now what? I need to pass the UPDATED edgeSet on to the next call, after the subtree rooted at 2 is dropped.</span>



    <span style="color:rgb(160,32,240)">then</span> edges (sndLogLine<span style="color:rgb(34,139,34)">:</span>logLines) edgeSet <span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic">Can't be an edge from first to second line; drop first line and keep going.</span>
    <span style="color:rgb(160,32,240)">else</span> edges (sndLogLine<span style="color:rgb(34,139,34)">:</span>(takeWhile (increasingIndent <span style="color:rgb(139,105,20)">$</span> length <span style="color:rgb(139,105,20)">$</span> fst fstLogLine) logLines))
         (Map.insertWith (<span style="color:rgb(139,105,20)">+</span>) ((fullName fstFields) <span style="color:rgb(139,105,20)">++</span> (fullName sndFields)) 1)
    <span style="color:rgb(160,32,240)">else</span> ((fst <span style="color:rgb(139,105,20)">$</span> edges (sndLogLine<span style="color:rgb(34,139,34)">:</span>logLines) edgeSet)
           <span style="color:rgb(139,105,20)">++</span> (fst <span style="color:rgb(139,105,20)">$</span> edges (fstLogLine<span style="color:rgb(34,139,34)">:</span>(drop
                                        (snd <span style="color:rgb(139,105,20)">$</span> edges (sndLogLine<span style="color:rgb(34,139,34)">:</span>logLines) edgeSet) <span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic"># of lines processed</span>
                                        logLines)) edgeSet),
          (snd <span style="color:rgb(139,105,20)">$</span> edges (sndLogLine<span style="color:rgb(34,139,34)">:</span>logLines) edgeSet)
          <span style="color:rgb(139,105,20)">+</span> (snd <span style="color:rgb(139,105,20)">$</span> edges (fstLogLine<span style="color:rgb(34,139,34)">:</span>(drop
                                      (snd <span style="color:rgb(139,105,20)">$</span> edges (sndLogLine<span style="color:rgb(34,139,34)">:</span>logLines) edgeSet) <span style="color:rgb(54,100,139);font-style:italic">-- </span><span style="color:rgb(54,100,139);font-style:italic"># of lines processed</span>
                                      logLines)) edgeSet)
         )

<span style="color:rgb(54,100,139);font-style:italic">--</span><span style="color:rgb(54,100,139);font-style:italic">--------------------------------------------------------------</span>
<span style="color:rgb(0,0,255)">fullname</span> <span style="color:rgb(139,105,20)">::</span> (<span style="color:rgb(34,139,34)">String</span>,<span style="color:rgb(34,139,34)">String</span>,<span style="color:rgb(34,139,34)">String</span>,[<span style="color:rgb(34,139,34)">String</span>]) <span style="color:rgb(139,105,20)">-></span> <span style="color:rgb(34,139,34)">String</span>
<span style="color:rgb(0,0,255)">fullname</span> (<span style="color:rgb(160,32,240)">_</span>,<span style="color:rgb(160,32,240)">_</span>,<span style="color:rgb(160,32,240)">_</span>,[<span style="color:rgb(160,32,240)">_</span>,fileName,directoryName]) <span style="color:rgb(139,105,20)">=</span> directoryName <span style="color:rgb(139,105,20)">++</span> fileName

<span style="color:rgb(54,100,139);font-style:italic">--</span><span style="color:rgb(54,100,139);font-style:italic">--------------------------------------------------------------</span>
<span style="color:rgb(72,61,139);font-style:italic">-- |Edges from the first line to all following lines</span>
<span style="color:rgb(0,0,255)">edgesFrom</span> <span style="color:rgb(139,105,20)">::</span> <span style="color:rgb(34,139,34)">String</span>             <span style="color:rgb(72,61,139);font-style:italic">-- ^ First line</span>
  <span style="color:rgb(139,105,20)">-></span> [<span style="color:rgb(34,139,34)">String</span>]                   <span style="color:rgb(72,61,139);font-style:italic">-- ^ Following lines</span>
  <span style="color:rgb(139,105,20)">-></span> <span style="color:rgb(34,139,34)">Map.Map</span> <span style="color:rgb(34,139,34)">String</span> <span style="color:rgb(34,139,34)">Int</span>         <span style="color:rgb(72,61,139);font-style:italic">-- ^ Set of edges built so far</span>
  <span style="color:rgb(139,105,20)">-></span> [<span style="color:rgb(34,139,34)">String</span>]
<span style="color:rgb(0,0,255)">edgesFrom</span> a b c <span style="color:rgb(139,105,20)">=</span> <span style="color:rgb(34,139,34)">[]</span>

<span style="color:rgb(54,100,139);font-style:italic">--</span><span style="color:rgb(54,100,139);font-style:italic">--------------------------------------------------------------</span>
<span style="color:rgb(72,61,139);font-style:italic">-- |Return length of indent or error</span>
<span style="color:rgb(0,0,255)">indentLength</span> <span style="color:rgb(139,105,20)">::</span> (<span style="color:rgb(34,139,34)">String</span>,<span style="color:rgb(34,139,34)">String</span>,<span style="color:rgb(34,139,34)">String</span>,[<span style="color:rgb(34,139,34)">String</span>]) <span style="color:rgb(72,61,139);font-style:italic">-- ^ Regex match context</span>
  <span style="color:rgb(139,105,20)">-></span> <span style="color:rgb(34,139,34)">Int</span>                                        <span style="color:rgb(72,61,139);font-style:italic">-- ^ Length of indent</span>
<span style="color:rgb(0,0,255)">indentLength</span> (prefix,<span style="color:rgb(160,32,240)">_</span>,<span style="color:rgb(160,32,240)">_</span>,<span style="color:rgb(34,139,34)">[]</span>) <span style="color:rgb(139,105,20)">=</span> error <span style="color:rgb(139,105,20)">$</span> <span style="color:rgb(178,34,34)">"Not matched: "</span> <span style="color:rgb(139,105,20)">++</span> prefix
<span style="color:rgb(0,0,255)">indentLength</span> (<span style="color:rgb(160,32,240)">_</span>,<span style="color:rgb(160,32,240)">_</span>,<span style="color:rgb(160,32,240)">_</span>,subexprs) <span style="color:rgb(139,105,20)">=</span>
  length <span style="color:rgb(139,105,20)">$</span> subexprs <span style="color:rgb(139,105,20)">!!</span> 0

<span style="color:rgb(54,100,139);font-style:italic">--</span><span style="color:rgb(54,100,139);font-style:italic">--------------------------------------------------------------</span>
<span style="color:rgb(72,61,139);font-style:italic">-- |Returns a list of edges, possibly with comments indicating occurrence counts > 1</span>
<span style="color:rgb(0,0,255)">edgeDump</span> <span style="color:rgb(139,105,20)">::</span> [(<span style="color:rgb(34,139,34)">String</span>,<span style="color:rgb(34,139,34)">Int</span>)]     <span style="color:rgb(72,61,139);font-style:italic">-- ^ List of (edge,count) tuples</span>
  <span style="color:rgb(139,105,20)">-></span> [<span style="color:rgb(34,139,34)">String</span>]                  <span style="color:rgb(72,61,139);font-style:italic">-- ^ List of edges, possibly w/comments</span>
<span style="color:rgb(0,0,255)">edgeDump</span> <span style="color:rgb(34,139,34)">[]</span> <span style="color:rgb(139,105,20)">=</span> <span style="color:rgb(34,139,34)">[]</span>
<span style="color:rgb(0,0,255)">edgeDump</span> ((edge,count)<span style="color:rgb(34,139,34)">:</span>rest)
  <span style="color:rgb(139,105,20)">|</span> count <span style="color:rgb(139,105,20)"><=</span> 1  <span style="color:rgb(139,105,20)">=</span> edge<span style="color:rgb(34,139,34)">:</span>(edgeDump rest)
  <span style="color:rgb(139,105,20)">|</span> otherwise   <span style="color:rgb(139,105,20)">=</span> (edge <span style="color:rgb(139,105,20)">++</span> <span style="color:rgb(178,34,34)">" /* "</span> <span style="color:rgb(139,105,20)">++</span> (show count) <span style="color:rgb(139,105,20)">++</span> <span style="color:rgb(178,34,34)">" occurrences */"</span>)<span style="color:rgb(34,139,34)">:</span>(edgeDump rest)

<span style="color:rgb(54,100,139);font-style:italic">--</span><span style="color:rgb(54,100,139);font-style:italic">--------------------------------------------------------------</span>
<span style="color:rgb(0,0,255)">first</span> <span style="color:rgb(139,105,20)">::</span> (a,b,c,d) <span style="color:rgb(139,105,20)">-></span> a
<span style="color:rgb(0,0,255)">first</span> (x,<span style="color:rgb(160,32,240)">_</span>,<span style="color:rgb(160,32,240)">_</span>,<span style="color:rgb(160,32,240)">_</span>) <span style="color:rgb(139,105,20)">=</span> x

<span style="color:rgb(0,0,255)">fourth</span> <span style="color:rgb(139,105,20)">::</span> (a,b,c,d) <span style="color:rgb(139,105,20)">-></span> d
<span style="color:rgb(0,0,255)">fourth</span> (<span style="color:rgb(160,32,240)">_</span>,<span style="color:rgb(160,32,240)">_</span>,<span style="color:rgb(160,32,240)">_</span>,x) <span style="color:rgb(139,105,20)">=</span> x
</pre></div></div></div></div><div id="gmail-postamble" class="gmail-status" style="color:rgb(0,0,0);font-family:"segoe ui";font-size:medium"><p class="gmail-author" style="font-size:14.4px;margin:0.2em"><br></p></div></div></div>