mkTupleOcc :: NameSpace -> TupleSort -> Arity -> OccName
mkTupleOcc ns sort ar = mkOccName ns str
  where
    -- No need to cache these, the caching is done in mk_tuple
    str = case sort of
                UnboxedTuple    -> '(' : '#' : commas ++ "#)"
                BoxedTuple      -> '(' : commas ++ ")"
                ConstraintTuple -> '(' : commas ++ ")"

    commas = take (ar-1) (repeat ',')

    -- Cute hack: we reuse the standard tuple OccNames (and hence code)
    -- for fact tuples, but give them different Uniques so they are not equal.
    --
    -- You might think that this will go wrong because isBuiltInOcc_maybe won't
    -- be able to tell the difference between boxed tuples and constraint tuples. BUT:
    --  1. Constraint tuples never occur directly in user code, so it doesn't matter
    --     that we can't detect them in Orig OccNames originating from the user
    --     programs (or those built by setRdrNameSpace used on an Exact tuple Name)
    --  2. Interface files have a special representation for tuple *occurrences*
    --     in IfaceTyCons, their workers (in IfaceSyn) and their DataCons (in case
    --     alternatives). Thus we don't rely on the OccName to figure out what kind
    --     of tuple an occurrence was trying to use in these situations.
    --  3. We *don't* represent tuple data type declarations specially, so those
    --     are still turned into wired-in names via isBuiltInOcc_maybe. But that's OK
    --     because we don't actually need to declare constraint tuples thanks to this hack.
    --
    -- So basically any OccName like (,,) flowing to isBuiltInOcc_maybe will always
    -- refer to the standard boxed tuple. Cool :-)
mkTupleOcc :: NameSpace -> TupleSort -> Arity -> OccName
mkTupleOcc ns sort ar = mkOccName ns str
  where
    -- No need to cache these, the caching is done in mk_tuple
    str = case sort of
                UnboxedTuple    -> '(' : '#' : commas ++ "#)"
                BoxedTuple      -> '(' : commas ++ ")"
                ConstraintTuple -> '(' : commas ++ ")"

    commas = take (ar-1) (repeat ',')

    -- Cute hack: we reuse the standard tuple OccNames (and hence code)
    -- for fact tuples, but give them different Uniques so they are not equal.
    --
    -- You might think that this will go wrong because isBuiltInOcc_maybe won't
    -- be able to tell the difference between boxed tuples and constraint tuples. BUT:
    --  1. Constraint tuples never occur directly in user code, so it doesn't matter
    --     that we can't detect them in Orig OccNames originating from the user
    --     programs (or those built by setRdrNameSpace used on an Exact tuple Name)
    --  2. Interface files have a special representation for tuple *occurrences*
    --     in IfaceTyCons, their workers (in IfaceSyn) and their DataCons (in case
    --     alternatives). Thus we don't rely on the OccName to figure out what kind
    --     of tuple an occurrence was trying to use in these situations.
    --  3. We *don't* represent tuple data type declarations specially, so those
    --     are still turned into wired-in names via isBuiltInOcc_maybe. But that's OK
    --     because we don't actually need to declare constraint tuples thanks to this hack.
    --
    -- So basically any OccName like (,,) flowing to isBuiltInOcc_maybe will always
    -- refer to the standard boxed tuple. Cool :-)
mkTupleOcc ns sort ar = mkOccName ns str
  where
    -- No need to cache these, the caching is done in mk_tuple
    str = case sort of
                UnboxedTuple    -> '(' : '#' : commas ++ "#)"
                BoxedTuple      -> '(' : commas ++ ")"
                ConstraintTuple -> '(' : commas ++ ")"

    commas = take (ar-1) (repeat ',')

    -- Cute hack: we reuse the standard tuple OccNames (and hence code)
    -- for fact tuples, but give them different Uniques so they are not equal.
    --
    -- You might think that this will go wrong because isBuiltInOcc_maybe won't
    -- be able to tell the difference between boxed tuples and constraint tuples. BUT:
    --  1. Constraint tuples never occur directly in user code, so it doesn't matter
    --     that we can't detect them in Orig OccNames originating from the user
    --     programs (or those built by setRdrNameSpace used on an Exact tuple Name)
    --  2. Interface files have a special representation for tuple *occurrences*
    --     in IfaceTyCons, their workers (in IfaceSyn) and their DataCons (in case
    --     alternatives). Thus we don't rely on the OccName to figure out what kind
    --     of tuple an occurrence was trying to use in these situations.
    --  3. We *don't* represent tuple data type declarations specially, so those
    --     are still turned into wired-in names via isBuiltInOcc_maybe. But that's OK
    --     because we don't actually need to declare constraint tuples thanks to this hack.
    --
    -- So basically any OccName like (,,) flowing to isBuiltInOcc_maybe will always
    -- refer to the standard boxed tuple. Cool :-)