开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜