I think this is overstated. Nobody would write out thousands of method definitions, just like nobody writes match statements with thousands of branches. Various combinations are combined simultaneously using wildcards, fallbacks, etc.
The methods would not be scattered, they would all be next to each other in the code (just like a match statement), because we are talking about sealed abstract types that behave like sum types, not open, extensible abstract types. So, the owner of the area function would implement all the applicable area methods for Shapes.
Yeah, that’s a good point, though I didn’t mean to imply that “pattern matching” via dispatch and argument destructuring is exactly equivalent to traditional pattern matching. I should have said a flavor of pattern matching. This flavor of pattern matching has the advantage that it already exists in the language right now. And I think it covers around 80% of typical pattern matching usage. But that might not be enough in Julia, because we’re greedy. 
I think the fact that dispatch uses method specificity doesn’t matter too much for the “pattern matching” usage with sealed abstract types that I described, since as far as I can tell we would almost always be dispatching on the concrete variants, so specificity doesn’t come into play, like in the area example:
area((; w, h)::Rectangle) = w * h
area((; w)::Square) = w * w
area((; b, h)::Triangle) = b * h / 2
I agree that Haskell example wouldn’t work with Julia method definitions, though, as a nitpick, you wouldn’t get method ambiguities. What would happen is that each new method would overwrite the previous method. Here’s an approximately analogous example with Shapes:
julia> foo((; w)::Rectangle, s::Square) = 1
foo (generic function with 1 method)
julia> foo(r::Rectangle, (; w)::Square) = 2
foo (generic function with 1 method)
julia> foo(r::Rectangle, s::Square) = 3
foo (generic function with 1 method)
julia> methods(foo)
# 1 method for generic function "foo" from Main:
[1] foo(r::Rectangle, s::Square)
If you want to have multiple “patterns” for a single variant, then you would just use a standard conditional inside the relevant ::Rectangle method, like this:
bar(r::Rectangle) = r.w > 10 ? "a" : "b"
Of course, if you combine dispatch and argument destructuring with if-else blocks inside methods, you get 100% of what you get from pattern matching. It’s just a question of whether it’s ergonomic enough.