class_docstr: An auto-batching DSL context.
  
  Auto-batching DSL operations are methods on the `ProgramBuilder` object.  It's
  used like this:
  
  ```python
  ab = dsl.ProgramBuilder()
  
  def fib_type(arg_types):
    return arg_types[0]
  
  with ab.function(type_inference=fib_type) as fibonacci:
    n = ab.param('n')
    ab.var.cond = ab.primop(lambda n: n > 1)
    with ab.if_(ab.var.cond):
      ab.var.nm1 = ab.primop(lambda n: n - 1)
      ab.var.fibm1 = ab.call(fibonacci, [ab.var.nm1])
      ab.var.nm2 = ab.primop(lambda n: n - 2)
      ab.var.fibm2 = ab.call(fibonacci, [ab.var.nm2])
      ab.var.ans = ab.primop(lambda fibm1, fibm2: fibm1 + fibm2)
    with ab.else_():
      ab.var.ans = ab.const(1)
    ab.return_(ab.var.ans)
  
  prog = ab.program(main=fibonacci)
  # Now `prog` is a well-formed `instructions.Program`, and the context
  # `ab` is no longer needed.
  ```
  
  Note the sequence of method calls on `ProgramBuilder` corresponds to the
  source code of the `Program` being defined, not its runtime behavior.  This is
  because (a) functions are defined with a context manager (rather than a Python
  function) which executes its body immediately and exactly once; and (b)
  function call instructions (and primitive operations) are just recorded, not
  entered recursively.
docstr: Records a conditional operation and `true` first branch.
  
  The `false` branch, if present, must be guarded by a call to `else_`, below.
  
  Example:
  ```python
  ab = dsl.ProgramBuilder()
  
  ab.var.true = ab.const(True)
  with ab.if_(ab.var.true):
    ...  # The body of the `with` statement gives the `true` branch
  with ab.else_():  # The else_ clause is optional
    ...
  ```
  
  Args:
    condition: Python string giving the boolean variable that holds the branch
      condition.
    then_name: Optional Python string naming the true branch when the program
      is printed.
    else_name: Optional Python string naming the false branch when the program
      is printed.
    continue_name: Optional Python string naming the continuation after the if
      when the program is printed.
  
  Yields:
    Nothing.
  
  Raises:
    ValueError: If trying to condition on a variable that has not been
      written to.
code: def if_(
      self,
      condition,
      then_name=None,
      else_name=None,
      continue_name=None,
  ):
      """Records a conditional operation and `true` first branch.
  
      The `false` branch, if present, must be guarded by a call to `else_`, below.
  
      Example:
      ```python
      ab = dsl.ProgramBuilder()
  
      ab.var.true = ab.const(True)
      with ab.if_(ab.var.true):
        ...  # The body of the `with` statement gives the `true` branch
      with ab.else_():  # The else_ clause is optional
        ...
      ```
  
      Args:
        condition: Python string giving the boolean variable that holds the branch
          condition.
        then_name: Optional Python string naming the true branch when the program
          is printed.
        else_name: Optional Python string naming the false branch when the program
          is printed.
        continue_name: Optional Python string naming the continuation after the if
          when the program is printed.
  
      Yields:
        Nothing.
  
      Raises:
        ValueError: If trying to condition on a variable that has not been
          written to.
      """
      # Should I have _prepare_for_instruction here?
      self._prepare_for_instruction()
      if condition not in self._var_defs:
          raise ValueError(
              "Undefined variable {} used as if condition.".format(
                  condition
              )
          )
      then_block = self._fresh_block(name=then_name)
      else_block = self._fresh_block(name=else_name)
      after_else_block = self._fresh_block(name=continue_name)
      self._append_block(
          then_block,
          prev_terminator=inst.BranchOp(
              str(condition), then_block, else_block
          ),
      )
      yield
      # In case the enclosed code ended in a dangling if, close it
      self._prepare_for_instruction()
      # FIXME: Always adding this goto risks polluting the output with
      # excess gotos.  They can probably be cleaned up during label
      # resolution.
      self._append_block(
          else_block, prev_terminator=inst.GotoOp(after_else_block)
      )
      self._pending_after_else_block = after_else_block
  
imports: import collections
  import contextlib
  import inspect
  from tensorflow_probability.python.experimental.auto_batching import instructions
label_desc: Use image node_id 14 for calling the ProgramBuilder obj's underlying member method code with example usage: obj.if_(condition, then_name, else_name, continue_name) without return types