Difference between struct and class in Common Lisp
Asked Answered
E

2

15

My understanding of a struct is that it has slots to store data in, has a type, has make-X and slot-accessor functions, and can be specialized on by a method (since it has a type).

My understanding of a class is that it has all of the same and multiple inheritance. The top answer to this question states that structs can have single inheritance, and that initial implementations of CLOS were "much slower" than structs.

Based on how people talk about CLOS and structs, I assume there must be some other differences, but my trivial google searches have been fruitless. So I ask: what are the practical differences between CLOS and structs?

Epigrammatize answered 12/12, 2015 at 20:52 Comment(0)
D
28

Structures

Structures are more primitive. They provide what often is called a record in programming languages. They were introduced in Common Lisp before classes. CLtL1 (the first book describing Common Lisp) in 1984 already had structures and later a standard object system called CLOS was added. Structures provide

  • a concise definition macro DEFSTRUCT
  • single inheritance
  • fast slot access
  • defines reader and setf access for slots
  • defines a type predicate
  • defines a constructor function
  • defines a copy function
  • a printed representation: structures can be read and printed
  • above functions maybe inlined

useful additions:

  • DEFSTRUCT can define list- and vector-based representations of structures, in addition to structure types

Limitations:

  • after a change to a structure type, structure instances are not updated
  • if a structure type is changed, it is best to recompile and re-run changed code. Maybe restart the program. The effects of redefining a structure are undefined in standard Common Lisp.
  • very little introspection: portable Common Lisp does not tell me super/sub-structures of a structure in an easy way. portable Common Lisp does not tell me the slots of a structure.
  • no slot access via slot names by default

Extensions

  • some implementations provide more runtime introspection and a little bit integration in some CLOS functions

CLOS classes

CLOS was invented in the mid/late 80s based on two earlier object systems (Flavors and LOOPS). It provides:

  • a defining macro DEFCLASS
  • multiple inheritance
  • protocols for creation, initialization, etc.
  • CLOS objects can be changed and updated at runtime based on class changes (new slots, redefined slots, removed slots, changed inheritance, ...)
  • CLOS objects can change their class and be updated at runtime
  • access via slot names possible

limitations:

  • no default printer/reader
  • DEFCLASS definitions are not very concise

extensions

  • faster slot access by added implementation specific functionality
  • Meta-Object Protocol provides added functionality and flexibility: introspection and reflection. Sometimes only parts of the MOP are provided.
  • user-provided extensions are available, especially for implementations with MOP support

Common Lisp

In some cases the Common Lisp standard does not say how a functionality should be implemented: structures, classes, or maybe even something else. Examples are streams and conditions. It's usually a good sign (for added flexibility) if a Common Lisp implementation uses CLOS for those.

Deuteron answered 12/12, 2015 at 23:25 Comment(5)
Out of curiosity, how does one test whether an implementation uses CLOS for (for example) streams?Epigrammatize
@Reepca: if a type is a subtype of standard-object, then it is a CLOS class. For a condition type: (subtypep 'error 'standard-object) -> T or NIL . Some implementations may allow, for example, various CLOS and non-CLOS types of streams.Deuteron
Could you elaborate on how changing classes at runtime is useful? I can't think of other languages that really have this so I never thought in those terms.Cru
@MasterMastic: change as in changing an object so that it is an instance of a different class? a) Think of anything long-running which you want to update: either in the development environment or an application. You may have a Lisp application for which you want to load a software patch without killing the application. b) a typical simple thing would be to change an object to a more special class once the application knows more about the thing. Oh, the TCP stream is actually a POP3 stream... change the stream instance to a pop3 stream class.Deuteron
Oh I see. I was talking about changing the class. As for the latter, it's common for any language, isn't it? e.g. a TCP -> POP3 function. As for the former, I think I would be hesitant to patch classes, it's probably not very good for the codebase, right? but I bet that's its own discussion. Thanks a lot!Cru
D
4

defstruct does more work for you behind the scenes, e.g.:

  1. defines slot accessors automatically
  2. defines readable print-object method

Also, structure slot access is faster (although the difference is probably inconsequential).

The bottom line is that, unless you need MOPish features, you can get away with defsrtuct.

Detached answered 12/12, 2015 at 23:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.