   Custom pretty printers can be implemented using
   functions, exported from this module.
 
+  Elixir implementation of the Wadler document algebra as described in 
+  "Strictly Pretty" (2000) by Christian Lindig
+  http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.34.2200
+   
+  The original Haskell implementation of the algorithm relies on lazy 
+  evaluation to unfold document groups on two alternatives:
+  _flat_ (breaks as spaces) and _broken_ (breakes as newlines).
+  Implementing the same logic on a strict language such as Elixir leads
+  to an exponential growth of the possible documents, unless document
+  groups are encoded explictly as _flat_ or _broken_. Those groups are
+  then reduced to a simple document, where the layout is already decided.
+
   ## Examples
 
     iex> Wadler.text "foo"                      
-    Wadler.TEXT[string: "foo"]
-    
-    iex> Wadler.group(Wadler.concat(Wadler.text("1"), Wadler.concat(Wadler.line, Wadler.text("2"))))
-    Wadler.UNION[
-      left: Wadler.CONCAT[
-        left: Wadler.TEXT[string: "1"], 
-        right: Wadler.CONCAT[left: Wadler.TEXT[string: " "], right: Wadler.TEXT[string: "2"]] 
-      ], 
-      right: Wadler.CONCAT[
-        left: Wadler.TEXT[string: "1"], 
-        right: Wadler.CONCAT[left: LINE, right: Wadler.TEXT[string: "2"]]
-      ]
-    ]
-
-  """
-
-  # Records to work with variant document representation
-  # Those are generalized by `docentity` type.
-  # Note that function that returns atom NIL is called `null`
-  @type docentity :: TEXT.t | CONCAT.t | UNION.t | NEST.t | GLUE.t | NIL | LINE
-  defrecord TEXT, string: ""
-  defrecord CONCAT, left: nil, right: nil
-  defrecord UNION, left: nil, right: nil
-  defrecord NEST, indent: 1, rest: nil
-  defrecord GLUE, string: "" 
-
-  # Records that represent finalized entities in a document
-  # Those are generalized by `docfactor` type.
-  @type docfactor :: Text.t | Line.t | Nil
-  defrecord Text, string: "", rest: nil
-  defrecord Line, indent: 0,  rest: nil
-
-  # Functional interface to `docentity` records
-  @doc """
-  Returns NIL which is a document entity used to represent
+    Wadler.DocText[str: "foo"]
+    
+    iex> doc = Wadler.group(Wadler.concat(Wadler.text("1"), Wadler.concat(Wadler.break, Wadler.text("2"))))
+    Wadler.DocGroup[
+      doc: Wadler.DocCons[
+        left: Wadler.DocText[str: "1"], 
+        right: Wadler.DocCons[
+          left: Wadler.DocBreak[str: " "], 
+          right: Wadler.DocText[str: "2"]
+        ]
+      ]
+    ]
+
+    iex> sdoc = Wadler.format w, 0, [{0, Wadler.Flat, doc}]
+    Wadler.SText[
+      str: "1", 
+      sdoc: Wadler.SText[
+        str: " ", 
+        sdoc: Wadler.SText[
+          str: "2",
+          sdoc: SNil
+        ]
+      ]
+    ]
+
+
+  """
+
+  # some shortcut functions
+  defp newline, do: "\n"
+  defp strlen(s), do: String.length(s)
+
+  defp repeat(_, 0), do: ""
+  defp repeat(s, i), do: String.duplicate s, i
+
+  # Records representing a __complex__ document
+  @type doc :: DocNil | DocCons.t | DocText.t | DocNest.t | DocBreak.t | DocGroup.t
+  defrecord DocCons, left: DocNil, right: DocNil
+  defrecord DocText, str: ""
+  defrecord DocNest, indent: 1, doc: DocNil
+  defrecord DocBreak, str: " "
+  defrecord DocGroup, doc: DocNil
+
+  # Functional interface to `doc` records
+  @doc """
+  Returns DocNil which is a document entity used to represent
   nothingness. Takes no arguments.
 
   ## Examples
 
-    iex> Wadler.null
-    NIL
-
-  """
-  @spec null() :: NIL
-  def null, do: NIL
+    iex> Wadler.empty
+    DocNil
+
+  """
+  @spec empty() :: DocNil
+  def empty, do: DocNil
 
   @doc """
   Concatenates two document entities. Takes two arguments:
-  left docentity and right docentity. Returns CONCAT 
-  docentity.
+  left doc and right doc. Returns a DocCons doc
 
   ## Examples
     
     iex(1)> Wadler.concat Wadler.text("Tasteless"), Wadler.text("Artosis")
-    Wadler.CONCAT[left: Wadler.TEXT[string: "Tasteless"], right: Wadler.TEXT[string: "Artosis"]]  
+    Wadler.DocCons[left: Wadler.DocText[str: "Tasteless"], right: Wadler.DocText[str: "Artosis"]]  
 
     iex(2)> IO.puts Wadler.pretty(80, v(1))
     TastelessArtosis
     :ok
 
   """
-  @spec concat(docentity, docentity) :: CONCAT.t
-  def concat(x, y), do: CONCAT[left: x, right: y]
+  @spec concat(doc, doc) :: DocCons.t
+  def concat(x, y), do: DocCons[left: x, right: y]
+  
 
   @doc """
   Nests document entity x positions deep. Nesting will be


  ## Examples

    iex(1)> Wadler.nest(5, Wadler.concat(Wadler.line, Wadler.text("6")))
    Wadler.NEST[indent: 5, rest: Wadler.CONCAT[left: LINE, right: Wadler.TEXT[string: "6"]]]

    iex(2)> IO.puts Wadler.pretty(80, v(1))
    

  ## Examples 

    iex(1)> Wadler.text "Hello, World!"
    Wadler.DocText[str: "Hello, World!"]

    iex(2)> IO.puts Wadler.pretty(80, v(9))
    Hello, World!
    :ok

  """
  @spec text(binary) :: DocText.t
  def text(s) when is_binary(s), do: DocText[str: s]

  
  @doc """
  Document entity representating a break. This break can
  be rendered as a linebreak or as spaces, depending on the
  `mode` of the chosen layout or the provided separator.
  
  ## Examples

    iex(2)> Wadler.break "hello"
    Wadler.DocBreak[str: "hello"]
   
    iex(3)> Wadler.break        
    Wadler.DocBreak[str: " "]

    iex(4)> Wadler.concat(
              Wadler.concat(
                Wadler.text("a"), Wadler.break), Wadler.text("b"))

    iex(5)> Wadler.render Wadler.format w, 0, [{0, Wadler.Flat, v(4)}]
    "a b"

    iex(6)> Wadler.render Wadler.format w, 0, [{0, Wadler.Break, v(4)}]
    "a\nb"

  """
  @spec break(binary) :: DocBreak.t
  def break(s) when is_binary(s), do: DocBreak[str: s]

  @spec break() :: DocBreak.t
  def break(), do: DocBreak[str: " "]

  @doc"""
  Inserts a break etween two docs.
  """
  @spec glue(doc, doc) :: DocCons.t
  def glue(x, y), do: concat(x, concat(break, y))

  @doc"""
  Inserts a break, passed as second argument, between two docs,
  the first and the third argument.
  """
  @spec glue(doc, binary, doc) :: DocCons.t
  def glue(x, g, y) when is_binary(g), do: concat(x, concat(break(g), y))

  @doc """
  Returns a group containing the specified document.
  ## Examples

    iex(1)> Wadler.group(
    ...(1)>   Wadler.concat(
    ...(1)>     Wadler.group(
    ...(1)>       Wadler.concat(
    ...(1)>         Wadler.text("Hello,"),
    ...(1)>         Wadler.concat(
    ...(1)>           Wadler.break,
    ...(1)>           Wadler.text("A")
    ...(1)>         )
    ...(1)>       )
    ...(1)>     ),
    ...(1)>     Wadler.concat(
    ...(1)>       Wadler.break,
    ...(1)>       Wadler.text("B")
    ...(1)>     )
    ...(1)> )) # Output is prettified for readability.
    Wadler.DocGroup[
      doc: Wadler.DocCons[
        left:  Wadler.DocGroup[
          doc: Wadler.DocCons[
            left:  Wadler.DocText[str: "Hello,"], 
            right: Wadler.DocCons[
              left:  Wadler.DocBreak[str: " "], 
              right: Wadler.DocText[str: "A"]
            ]
          ]
        ], 
        right: Wadler.DocCons[
          left:  Wadler.DocBreak[str: " "], 
          right: Wadler.DocText[str: "B"]
        ]
      ]
    ]

    iex(2)> IO.puts Wadler.pretty(80, v(1))
    Hello, A B
