Basically all of this boils down to some simple principles. I will also shed light on some fundamental design patterns at play here later on.
Data types are individual, independent sets of values obtained via constructors, which, figuratively, are magic functions without a defining body that "spawn" values ex nihilo, sort of:
data DataType = Constr1 | Constr2
sometimes you want to embed values from another type into the values of your type; you do that via parametric constructors:
data DataType = Constr1 | Constr2 | Constr3 Int
now values of type Int can be embedded within the values of DataType, but only as part of values constructed with Constr3.
Now, what should be becoming obvious, is that you can't simply put values of type Int directly into the set defined by DataType:
data DataType = Constr1 | Constr2 | Constr3 Int | 1 | 2 | 3
— no, that does not work because that would lead to weird stuff like the values 1, 2 and 3 being of the type DataType, while it's obvious they are (also) of type Int:
1 :: Int
1 :: DataType -- what you are attempting
while at the same time, some other values in Int are not in DataType:
4 :: DataType -- you want to AVOID this.
so as you can see, this has quickly led us to absurdity. And that's exactly what you were unknowingly referring to when you said "it doesn't allow me to call out any numbers".
So let's go back to "wrapping" constructors as already exemplified by Constr3:
data Rank = Lower Int | Jack | Queen | King | Ace
what this allows us to do is:
Jack :: Rank -- of course
Ace :: Rank -- of course
Lower 3 :: Rank -- obviously
but also
Lower 999 :: Rank -- what?
which almost completely defeats the purpose of having a type-safe Rank altogether, so we might as well just use integers 2 throughout 14 to indicate any rank, giving up static type safety — but we don't want that, as we'd then have to be concerned with avoiding running into anything below 2 or above 14, ending up sprinkling the code with boring, uninformative and unnecessary runtime checks.
Hence, while a solution such as this techically solves the issue — there is no compilation error and you can write your program — you'd constantly be on the verge, in need of ensuring there aren't some lower ranks gone rogue, living somewhere in your card game, pretending to be 999 or 1 000 000 and so on.
So here's the solution that I'd recommend instead:
data Rank = R2 | R3 | R4 | R5 | R6 | R7 | R8 | R9 | R10
| Jack | Queen | King | Ace
deriving Show
this way you will never ever have to check the validity of a Rank value once it's been constructed. You can always safely pattern match over ranks without getting spurious pattern exhaustion warnings at compile time, or pattern match failures at runtime.
To sum up, this "pattern" of design is often referred to as Correctness by Construction, Correctness by Design, Type Guided programming as well as the phrase "Make illegal states unrepresentable" etc. This design philosophy is also used in F#, OCaml, Scala and other statically typed programming languages (especially functional ones).
Furthermore: now that you have a nice flat and safe Rank data type, you might want to consider making your Rank an instance of Eq and Ord, getting free comparisons:
data Rank = R2 | R3 | R4 | R5 | R6 | R7 | R8 | R9 | R10
| Jack | Queen | King | Ace
deriving (Eq, Ord, Show)
yielding:
Prelude> R2 == R3
False
Prelude> Ace > Queen
True
Prelude> R9 < Jack
True
Prelude> R9 <= R9
True
or if you want to be able to convert to and from integers, take a look also at Enum.