If your code is Haskell 2010, with no language extensions turned on, Haskell actually doesn't support run-time polymorphism at all!
That's quite surprising. Haskell feels like a very polymorphic language. But in fact, all the types can in principle be decided purely at compile-time. (GHC chooses not to, but that's an implementation detail.)
This is exactly the same situation as C++ templates. When you write something like std::vector<int>, the compiler knows, at compile-time, all the times involved. The really surprising thing is just how rarely you actually need true run-time polymorphism!
Now, there are scenarios where you want to run different code based on run-time circumstances. But in Haskell, you can do that just by passing a function as an argument; you don't need to create a "class" (in the OOP sense) merely to achieve this.
Now, if you turn on some language extensions (most conspicuously ExistentialQuantification) then you get true, run-time polymorphism.
Note that the main reason people seem to do this is so you can do
class Foo f where
bar :: f -> Int
baz :: f -> Bool
data AnyFoo = forall f. Foo => AnyFoo f
my_list :: [AnyFoo]
...
This is widely considered a Haskell anti-pattern. In particular, if you upcast stuff in Java to put it into a list, you then later downcast it again. But the code above offers no possibility to ever downcast. You also can't use run-time reflection (since Haskell doesn't have that either). So really, if you have a list of AnyFoo, the only thing you can do with it is call foo or bar on it. So... why not just store the result of foo and bar?
data AnyFoo = AnyFoo {foo :: Int, bar :: Bool}
It lets you do exactly the same stuff, but doesn't require any non-standard extensions. In fact, in some ways, it's actually a bit more flexible. You now don't even need a Foo class, you don't need to define a new type for every sort of Foo you might have, just a function that constructs the AnyFoo data structure for it.