Package versioning policy - Harmless type changes?
The package versioning policy specifies that changing the type of any entity makes a change of the B number in A.B.C necessary.
However, it seems to me that some type changes don't break dependent code. In particular, consider the following example, where I drop a Typeable
class constraint:
- foo :: Typeable a => AddHandler a -> NetworkDescription (Event a)
+ foo :: AddHandler a -> NetworkDescription (Event a)
So, my question is:
开发者_Python百科Can removing a type class constraint on a function break dependent code? Should I change the B number or just the C in version A.B.C when introducing this change?
I have replied on -cafe, but I’ll also put my answer here:
You should bump the C number. The PVP, rule 2, specifies that an API addition implies that the C part of the version is to be increased. Removing a constraint behaves like adding a new function: Code that worked before continues to work, but code written against the new API might not work against the old one.
So if a programmer develops code against version 0.1.2 of foo, he’d specify foo >= 0.1.2 && < 0.2
as the requirement. He does not expect his code to work against foo-0.1.1. This works fine with removing the constraint.
Can removing a type class constraint on a function break dependent code?
No, I don't believe so. Here's my reasoning. It's common to think of constraints as an additional function argument: a typeclass dictionary which is passed implicitly. You can think slightly more general than that, and imagine that constraints are a record of evidences which is passed implicitly. Thus, the type Foo
can be thought of as {} -> Foo
, where {}
indicates an empty record of implicit evidences. It is irrelevant whether Foo
itself is a function or not.
Now, suppose that our API promises to deliver something of type
{SomeConstraint} -> Foo
But what we actually deliver is
{} -> Foo
Well, record subtyping tells us that
{SomeConstraint} <: {}
therefore function subtyping tells us that
({} -> Foo) <: ({SomeConstraint} -> Foo)
therefore, anywhere in someone's program that we find a hole with the shape {SomeConstraint} -> Foo
, we can plug in a {} -> Foo
. In other words, nothing breaks. The user gives us evidence that SomeConstraint
is satisfied, and we just ignore it.
At first I thought that weird corner cases in type inference might cause problems, but I cannot think of any examples where this is actually the case. So this thought was proven wrong by exhaustion of imagination.
精彩评论