Returns a phased version of the effect.

    For example, an X gate phased by 90 degrees would be a Y gate.
    This works by calling `val`'s _phase_by_ method and returning
    the result.

    Args:
        val: The value to describe with a unitary matrix.
        phase_turns: The amount to phase the gate, in fractions of a whole
            turn.  Divide by 2pi to get radians.
        qubit_index: The index of the target qubit the phasing applies to. For
            operations this is the index of the qubit within the operation's
            qubit list. For gates it's the index of the qubit within the tuple
            of qubits taken by the gate's `on` method.
        default: The default value to return if `val` can't be phased. If not
            specified, an error is raised when `val` can't be phased.

    Returns:
        If `val` has a _phase_by_ method and its result is not NotImplemented,
        that result is returned. Otherwise, the function will return the
        default value provided or raise a TypeError if none was provided.

    Raises:
        TypeError:
            `val` doesn't have a _phase_by_ method (or that method returned
            NotImplemented) and no `default` was specified.

def phase_by(val: Any, phase_turns: float, qubit_index: int,
             default: TDefault = RaiseTypeErrorIfNotProvided):
    """Returns a phased version of the effect.

    For example, an X gate phased by 90 degrees would be a Y gate.
    This works by calling `val`'s _phase_by_ method and returning
    the result.

    Args:
        val: The value to describe with a unitary matrix.
        phase_turns: The amount to phase the gate, in fractions of a whole
            turn.  Divide by 2pi to get radians.
        qubit_index: The index of the target qubit the phasing applies to. For
            operations this is the index of the qubit within the operation's
            qubit list. For gates it's the index of the qubit within the tuple
            of qubits taken by the gate's `on` method.
        default: The default value to return if `val` can't be phased. If not
            specified, an error is raised when `val` can't be phased.

    Returns:
        If `val` has a _phase_by_ method and its result is not NotImplemented,
        that result is returned. Otherwise, the function will return the
        default value provided or raise a TypeError if none was provided.

    Raises:
        TypeError:
            `val` doesn't have a _phase_by_ method (or that method returned
            NotImplemented) and no `default` was specified.
    """
    getter = getattr(val, '_phase_by_', None)
    result = NotImplemented if getter is None else getter(
        phase_turns, qubit_index)

    if result is not NotImplemented:
        return result
    if default is not RaiseTypeErrorIfNotProvided:
        return default

    if getter is None:
        raise TypeError("object of type '{}' "
                        "has no _phase_by_ method.".format(type(val)))
    raise TypeError("object of type '{}' does have a _phase_by_ method, "
                    "but it returned NotImplemented.".format(type(val)))