emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Changing a cl-defstruct definition in a published package


From: João Távora
Subject: Re: Changing a cl-defstruct definition in a published package
Date: Fri, 13 Jul 2018 18:01:29 +0100

On Thu, Jul 12, 2018, 21:13 Clément Pit-Claudel <address@hidden> wrote:
Hi all,

I'm sure this has already been discussed, but I couldn't find the relevant discussion.  I'm running into issues trying to update a package without breaking other packages that depend on it.  Advice would be very welcome.

I maintain package A, which contains this:

  (cl-defstruct aaa-info
    line message)

  (provide 'aaa)

Someone else wrote a package B that contains this:

  (require 'aaa)

  (defun bbb-print-message (info)
    (message (aaa-info-message info)))

  (provide 'bbb)

Users have installed both packages through package.el.  I update A by adding a new field to the definition of aaa-info, and push the update to ELPA or MELPA:

  (cl-defstruct aaa-info
    line column message)

Users update using package.el, but this does not cause b to be recompiled.  From this point on, any subsequent call to bbb-print-message to bbb-print-message fails: for example, (bbb-print-message (make-aaa-info :line 1 :column 2 :message "XYZ")) prints this:

  Debugger entered--Lisp error: (wrong-type-argument stringp 2)
    message(2)
    bbb-print-message(#s(aaa-info :line 1 :column 2 :message "XYZ"))

Similarly, if I update the constructor of aaa-info to give the new 'column' field a default value, instances created by the previously-compiled B will still be missing the 'column' slot, leading to all sorts of errors.

What is the recommended way to change a cl-defstruct definition without running into these issues?

Thanks!
Clément.

Hi Clément,

There's no way to fix the problem now without breaking backward compatibility because by now B's use of your accessor has been compiled into something that probably looks like an aref into an array.  So if you change your object layout in A, you break a compiled B.

The way to avoid this beforehand is not to expose the defstruct's accessors as a public interface.  You can't think of them as functions, because they have compiler macros associated.  The easiest "solution" now is to make proper functions for those accessors that are public, and then having B recompiled.  Also consider if you really need defstructs for their speed, because they're usually an array in disguise.  Perhaps a (slightly, in most cases) slower eieio class would do. 

João

reply via email to

[Prev in Thread] Current Thread [Next in Thread]