I'm curious if there's any precedent for wrapper objects or types in Emacs Lisp. Javascript notably has built-in types that wrap built-in literal types and those objects can largely be used in the fashion of their literal counterpars via the .valueOf() method. For example:

new String("foo") + ", " + new String("bar")
=> "foo, bar"

Naturally, since they are objects the behavior is not identical to literals and the result may be more or less surprising given the experience of the programmer:

new Boolean(false) || new Boolean(true)
=> Boolean {[[PrimitiveValue]]: false}

Another example, Java has java.lang.Number and similar types, but IIRC doesn't offer any overloading/syntactic sugar.

Edit: Programmers use these because it affords them something worth the cost of dereferencing the value. I suggest there's no quality of object-oriented languages that makes them uniquely capable of expressing or employing boxed values and neither Elisp that otherwise makes it incapable of using boxed values. It's simply a programming pattern and I am asking about prior art in the context of Emacs Lisp.

A wrapped value in Elisp may be as simple as:

(list 3)
=> (3)

Rather than simply being a literal, it is also a generalized variable; it is setf'able, there is a place (cdr) to attach data to your heart's content, separate, but equal values are distinct with respect to #'eq, and even the greenest lisper can unbox the value. Certainly this implementation is not without its drawbacks and therein lies the crux. Elisp is quite powerful; many things are arguably Bad™, but I think it's still worthwhile to think creatively about what's possible.

  • Hard to imagine what the need would be, for Lisp. But you can of course wrap a primitive (or any other function) with additional code. A particular case of this is advising a function. But pose a specific question about something that you are trying to do, if that interests you. – Drew Apr 22 '17 at 1:36
  • @Drew It's mostly a matter of curiosity. Elisp has text properties and symbol plists which are very useful for annotating strings and symbols respectively. Just wondering what similar tools may have been conceived of for extending arbitrary literals or lists which generally preserve their behavior. Advice would be useful for preserving behavior against select functions, but probably wouldn't be a useful global mechanism. – ebpa Apr 22 '17 at 3:05
  • "Naturally, since they are objects the behavior is not identical to literals" - This is rather unnatural (maybe it's justified to increase performance), but in Elisp (and most other lisps) literals are exactly the same as constructed objects (because the literal just tells the reader to construct the object). – npostavs Apr 23 '17 at 16:58

Javascript and Java only do this because they are object-oriented languages. They want to be able to put methods on things (because that's what you do in object-oriented languages) therefore strings and numbers must be objects. On the other hand, objects aren't as efficient as primitive numbers or booleans (or even strings, although in Javascript all strings are always objects even if you never called new String()). You should also know that the proper term for it is "boxing". When you "box" a primitive type, you get back an instance object that implements similar semantics, but that you can call methods on.

There's nothing similar in Elisp, because there is no syntax similar to a method call. With no pressure to add methods to everything, there's no need for boxed values either. Even when you define structures (using defstruct), the accessor "methods" for the slots you define are just ordinary top-level functions like any other. Common Lisp takes this a step (or two) further with multi-methods, where all arguments to a function may be objects, and method dispatch must examine all of them to determine which method implementation to call. They're still just top-level functions though, with no special syntax, so Common Lisp doesn't have boxing either.

  • I'm glad you mentioned boxing; that is a more apt descriptor (though maybe boxing ⊂ wrapping?). I disagree with your argument however. I've updated the question with regard to your answer. – ebpa Oct 4 '17 at 1:08

Java has that weird distinction between value types and reference types. Primitive types are passed by value, their values compare with ==, and cannot be cast to Object. Reference types are passed by reference, their identities compare with ==, their values compare with .equals, and they can be cast to Object in order to be passed to a polymorphic function or stored in a polymorphic collection.

Lisp-family languages have a pure reference semantics: to the programmer, all types behave like reference types (whether they are actually implemented as references or not). All types compare their identities with eq, their values with equal, and values of any type can be stored in a polymorphic sequence (e.g. a list) without further wrapping:

(list 2 "hi!" (current-buffer))

Even better, methods can be attached to any type:

(defmethod foo ((x integer))
    (message "Hi!"))
(foo 5)

Since immediate values have all the good properties of reference types, Java-style wrapping is seldom if ever necessary in Lisp. In the rare cases when it's needed (e.g. in order to have a mutable object), it's simple enough to create a one-field defstruct or a singleton list.

(Aside: Lisp implementations do a fair amount of magic behind the scenes to make this pure reference semantics efficient. The main techniques are the use of tagged pointers, which allow storing an immediate value (such as a small integer) within the pointer itself, and interning, which allows the very common symbol type to be compared using eq and avoids memory allocation when the same symbol is used multiple times.)

  • I didn't know cl dispatched on number/float/integer/string/symbol! Even cooler that cl has the desired specializer behavior with respect to number versus float/integer! – ebpa Oct 4 '17 at 1:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.