Ad-hoc polymorphism erodes type-safety

And this, I suppose, relies on library authors agreeing to write monomorphic length outside of their Foldable instances and referencing that when implementing Foldable, as opposed to implementing it in the instance body?

That’s probably a good idea for other reasons, too, but it is more verbose and not obviously better if you haven’t read any of this.

Interestingly, and I just thought about it, how in common popular languages you can write

vec.length
array.length
list.length
tree.length

Which is exactly the same number of characters as

length vec
length array
length list
length tree

But you automatically have guarantees that your methods are called for a specific implementation*. No need to import length qualified and worry about such problems with such a common function like length at all.

* unless such length is not something like traits in Rust

I don’t see how you don’t have the exact same problem in those languages, e.g. in Java you could write something like:

myFunction(Maybe<List<Int>> list) {
   ... list.length ... // oops
}

(assuming that Maybe exists and has a length field)

1 Like

Sure, if you have a guarantee that vec is a Vec, but the point of the article is that you call length settingAllowList, or settingAllowList.length if you like, without a guarantee that it’s actually a list. So when the type of settingAllowList changes to Maybe something then having written settingAllowList.length doesn’t help at all.

2 Likes

I think I would link this with type blindness (or boolean blindness, algebraic blindness, I don’t know what term to use). Which seems to be a similar link to the structure-obliviousness from @ChShersh.

For the specific example of the article, if there weren’t explanations surrounding, I’d be hard pressed to understand what the difference between Nothing and Just [] is supposed to be, whereas something like Anyone and Some [] would mean a bit less mental gymnastics. Though an empty list is still weird so a third constructor might be of interest. In that case, a new data type for the specific business need would have the compiler error on the missing implementation for Foldable.

Although I’m only thinking of this because I’ve just been made aware of this problem, and can link it to other problems. And I’m just shifting the monomorphization from the functions to the data.

I had a bug of this kind involving the Monad typeclass once. It looked something like this:

Before:

foo :: [Bar] → IO ()
foo xs = do
   this xs
   that

After refactoring:

foo :: [Bar] → IO ()
foo = do
    this'
    that'

I was quite surprised to find out that the IO action associated with that' was never run.