Yes, this is a completely idiomatic style. This is a classic way to make tail function recursive. (Although this is less important and a bit more subtle in Haskell).
In addition, creating helper functions is certainly a good and key part of many common patterns. If you feel something more natural, check it out! If it is not taken to extremes, it helps to read.
The main suggestion I have is to put a helper function in the where clause. Thus, it will be visible only within the framework of the main function, which makes it clear that this is just an assistant. The name you give to the helper function is less important; splitCards_ great, but splitCards' (pronounced splitCards prime) and go (common name for helper functions) will be more common. So I would rewrite your code something like this:
splitCards xs = go xs ([],[],[]) where go (x:xs) (a, b, c) | isGold x = go xs (a:x,b,c) | isAction x = go xs (a,b:x,c) | isVP x = go xs (a,b,c:x) go [] (a, b, c) = (a, b, c)
Note that these are just cosmetic changes - what you do sounds fundamentally.
Two naming options ( go vs splitCards' ) are just a matter of preference.
I personally like go , because as soon as you get used to the agreement, it clearly signals that something is just an auxiliary function. In a sense, go almost more like syntax than native function; this value is purely subordinate to the splitCards function.
Others do not like go , because it is a little mysterious and somewhat arbitrary. It can also make the recursive structure of your code less clear, because you are recursing to go , and not to the function itself.
Go for what you think is best.