Keep Working, Worker Bee!

5.10.2006

Package interfaces

So we all know that you should program to an interface, not an implementation, if you're writing a program in the modular, component-oriented world. In Java or ML, at a basic level this is pretty much straightforward to do: you get some object with a known interface (or you import some structure with a known signature) and start referring to its fields/methods/values/types however you'd want, by saying some variant of "I want resource x from component C" every time you want something. Every time you want something from a component, you write down what you want, and from which component you want it. If you name something that you don't know to exist, the compiler yells at you and that's the end of the story. If the maintainer of the component you're using adds a new resource to the interface tomorrow, that doesn't affect your code at all.

In PLT Scheme the situation is a bit worse. When you import a module, you just name it and all its provided names get dumped into your namespace. If you import two modules, they must have completely distinct namespaces, or mzscheme doesn't know which one you wanted and signals an error. This has an insidious effect that becomes apparent as programs evolve: if you import a module, you're not only relying on it providing some names, but also on it not providing any other names. That means that if you start using somebody else's component and that person adds a new resource to its interface, your code might break.

This problem has always existed, but PLaneT turned it from a hypothetical problem to a real one. It has a tendency to do that ...

5 Comments:

  • This is not true for adding methods to a class, nor is it true for functors that take structures without prefixes (a situation similar to the one you describe for mz; lets not forget that one can add prefixes to mz module imports)

    By Anonymous Anonymous, at 15:27  

  • You're right of course that you can get the same problems I mentioned in Java or ML, but I didn't mean to suggest that these problems were impossible to create in those languages, just that they don't come up if you use things in basic ways. In Java you're only in danger if you write a class that inherits from somebody else's class that's not under your control; it doesn't happen if you just have an object of that class's type. I don't have hard numbers but I'd expect that this situation is pretty rare. (You can create a mess for yourself this same way, but in this situation I was really talking about somebody else making changes that affect your code's correctness, not you refactoring things for yourself.)

    In ML, you can get a similar problem if you use somebody else's structure, but only if you use open rather than prefixes to access to get at its components. The first thing that any MLer will tell you about open is never to use it, and that's the style in which every so I don't really count that as basic usage either.

    By Blogger Jacob Matthews, at 15:57  

  • Python doesn't have this problem - you can choose from "import X" or "from X import *"

    By Anonymous Anonymous, at 17:58  

  • Hi David,

    Just to give background: mzscheme allows prefix import:

    (require (prefix list: (lib "list.ss")))

    and moreover, there is the option to require only specific things from a module:

    (require (only (lib "list.ss") empty))

    So the issue isn't a matter of not being able to prevent this problem. The problem is a social one. The default approach to loading a module:

    (require (lib "list.ss"))

    is the idiom that most PLT Schemers use right now; it's almost equivalent to what Python programmers will do with 'from Tkinter import *'. Having people use the prefix form defensively will take some getting used to.


    Python suffers from related --- and worse ---problems in this domain: Python will merrily let 'from os import *' overwrite the builtin open() primitive. There's a reason why 'from foo import *' is highly discouraged in the Python community: there's no safety net when names collide. In PLT Scheme, at least, the module system will say when names will collide.

    Technical points aside: I think you're trying to point out that the prefix approach is the one that's the default in Python (and many other language communities), and that's why Python programmers don't run into this problem as often as PLT Scheme programmers. That would be a valid point.

    By Blogger Danny Yoo, at 02:45  

  • To follow on from Danny,
    I believe there is a way to query libraries so you know what names they will import.
    I can't remember what it is.

    By Blogger Stephen, at 22:48  

Post a Comment

<< Home