Whether it's better to model something as a sum-type or a type-class is I think an intuition that you simply build over time (sum-types are more often the better option).
But since you mention
I know typeclasses are needed for ad-hoc polymorphism in Haskell, but I'm not sure how to piece it together.
here's a hopefully intuitive explanation.
Basic idea
Basically you're saying that there is a whole selection of types that your code can work with. You should try to formulate what those types have in common - i.e. why do you accept those types but reject others. You then define a type-class that captures this and implement most of your code in terms of this type-class.
Your example
To illustrate it on your example.
You're accepting numbers and strings (that represent numbers) because you say that both can be represented as sequences of digits and you want to define a function that counts those digits. It probably then makes sense to define a type-class that captures the ability to be written as a sequence of digits.
class IsBase10Positional t where
digits :: t -> [Int]
you'd then define the instances for your types:
instance IsBase10Positional Int where
digits n = D.digits 10 n
instance IsBase10Positional String where
digits chars = digitToInt <$> chars
D.digits
comes from http://hackage.haskell.org/package/digits-0.3.1/docs/Data-Digits.html#v:digits
digitToInt
from https://hackage.haskell.org/package/base-4.15.0.0/docs/Data-Char.html#v:digitToInt
your numDigits
function is then defined on all types that are part of this type-class:
numDigits :: IsBase10Positional a => a -> Int
numDigits x = length (digits x)
Conclusion
It is often pretty hard to formulate what is the common behavior that the type-class is supposed to capture. If you start adding a lot of functions into the type-class itself then you probably failed to capture the right essence of what the types have in common (in your application's domain).
data MyNum = IntNum Int | StringNum String
good enough? (if you do aNum
andIsString
instance for that type you can getnumDigits ..
as you wanted working with theOverloadedStrings
extension – Random Dev