productUnion :: (Ord sym, Ord sta1, Ord sta2) => Set sym -> Fa sym sta1 -> Fa sym sta2 -> Fa sym (sta1, sta2)
productUnion symbols fa1@(Fa initialStates1 finalStates1 transitions1) fa2@(Fa initialStates2 finalStates2 transitions2) =
  let
    transitions =
      symbols `andThen` (\symbol ->
      transitions1 `andThen` (\(Transition symbol1 source1 target1) ->
      transitions2 `andThen` (\(Transition symbol2 source2 target2) ->
      guard (symbol1 == symbol && symbol2 == symbol) `andThen` (\_ ->
      Helpers.return $ Transition symbol (source1, source2) (target1, target2)))))
  in
    Fa
      (initialStates1 `andThen` (\initial1 -> initialStates2 `andThen` (\initial2 -> Helpers.return (initial1, initial2))))
      (Set.union
        (finalStates1 `andThen` (\final1 -> states fa2 `andThen` (\states2 -> Helpers.return (final1, states2))))
        (states fa1 `andThen` (\states1 -> finalStates2 `andThen` (\final2 -> Helpers.return (states1, final2)))))
      transitions
productUnion :: (Ord sym, Ord sta1, Ord sta2) => Set sym -> Fa sym sta1 -> Fa sym sta2 -> Fa sym (sta1, sta2)
productUnion symbols fa1@(Fa initialStates1 finalStates1 transitions1) fa2@(Fa initialStates2 finalStates2 transitions2) =
  let
    transitions =
      symbols `andThen` (\symbol ->
      transitions1 `andThen` (\(Transition symbol1 source1 target1) ->
      transitions2 `andThen` (\(Transition symbol2 source2 target2) ->
      guard (symbol1 == symbol && symbol2 == symbol) `andThen` (\_ ->
      Helpers.return $ Transition symbol (source1, source2) (target1, target2)))))
  in
    Fa
      (initialStates1 `andThen` (\initial1 -> initialStates2 `andThen` (\initial2 -> Helpers.return (initial1, initial2))))
      (Set.union
        (finalStates1 `andThen` (\final1 -> states fa2 `andThen` (\states2 -> Helpers.return (final1, states2))))
        (states fa1 `andThen` (\states1 -> finalStates2 `andThen` (\final2 -> Helpers.return (states1, final2)))))
      transitions
productUnion symbols fa1@(Fa initialStates1 finalStates1 transitions1) fa2@(Fa initialStates2 finalStates2 transitions2) =
  let
    transitions =
      symbols `andThen` (\symbol ->
      transitions1 `andThen` (\(Transition symbol1 source1 target1) ->
      transitions2 `andThen` (\(Transition symbol2 source2 target2) ->
      guard (symbol1 == symbol && symbol2 == symbol) `andThen` (\_ ->
      Helpers.return $ Transition symbol (source1, source2) (target1, target2)))))
  in
    Fa
      (initialStates1 `andThen` (\initial1 -> initialStates2 `andThen` (\initial2 -> Helpers.return (initial1, initial2))))
      (Set.union
        (finalStates1 `andThen` (\final1 -> states fa2 `andThen` (\states2 -> Helpers.return (final1, states2))))
        (states fa1 `andThen` (\states1 -> finalStates2 `andThen` (\final2 -> Helpers.return (states1, final2)))))
      transitions