Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

A `define-class` macro for less boilerplate

License

NotificationsYou must be signed in to change notification settings

atlas-engineer/nclasses

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NClasses provides helper macros to help write classes and conditions with less boilerplate.

It’s a fork ofhu.dwim.defclass-star.

Motivation

  • hu.dwim.defclass-star has a symbol export bug which cannot be fixed upstream, seehu-dwim#7 andhu-dwim#12 for a discussion.
  • The macro and package names of hu.dwim.defclass-star prove to be rather unwieldy. Emacs can automatically highlightdefine-class as a macro, but notdefclass*.
  • This library offers new features that wouldn’t be accepted upstream, liketype inference.
  • This library goes beyond class definitions in providing more utility macros e.g.define-generic andmake-instance*.

Examples

A basic session:

(define-class foo ()  ((slot1:initargnil)   (slot2 \"hello!\")   (unexported-slot :export nil))  (:export-class-name-p t)  (:export-accessor-names-p t)  (:accessor-name-transformer #'nclasses:default-accessor-name-transformer))(make-instance 'foo :my-slot1 17)

See thepackage documentation for a usage guide and more examples.

Default class options

If you want to change the default class options, say, for a package, you can simply define a wrapping macro (without importingnclasses:define-start):

(defmacrodefine-class (name supers slots&rest options)"`nclasses:define-star' with automatic types and always-dashed predicates."`(nclasses:define-class,name,supers,slots,@(append'((:automatic-types-pt)          (:predicate-name-transformer'nclasses:always-dashed-predicate-name-transformer))        options)))

Helpers beyonddefine-class

define-generic

define-generic is made to shorten the frequent pattern of generic with one method:

(defgenericfoo (a b c)  (:method ((ainteger) (bsymbol) c)    (bar))  (:documentation"Some FOO documentation."))

Such a scary bloated code often makes one to use the neatdefmethod instead:

(defmethodfoo ((ainteger) (bsymbol) c)"Some FOO documentation."  (bar))

While convenient and short, standalone method definition auto-generates a generic function that’s neither documented nor inspectable.define-generic solves this problem by makingdefgeneric form shorter and moredefmethod-like, without any loss of semantics. The previous form looks like this withdefine-generic:

(define-generic foo ((ainteger) (bsymbol) c)"Some FOO documentation."  (bar))

This form expand to exactly the same generic definition as the one above, while being as concise as the defmethod version.

The body ordefine-generic is automatically wrapped into a:method option, so there could be several body forms. If any of these body forms is adefgeneric option, it’s safely put as defgeneric option outside the implied method:

(define-generic foo ((ainteger) (bsymbol) c)"Some FOO documentation."; Docstring should always go first.  (:method-combination progn)  (bar)  (:generic-function-class foo-class));; =>;; (defgeneric foo (a b c);;   (:method ((a integer) (b symbol) c);;     (bar));;   (:method-combination progn);;   (:generic-function-class foo-class);;   (:documentation "Some FOO documentation."))

See thedefine-generic documentation for more examples and details.

:export-generic-name-p (option) and \*export-generic-name-p\* (variable)

These allow to export generic name after defining it:

(define-generic foo ((ainteger))  (bar a)  (:export-generic-name-pt))

make-instance*

There are several idioms that heavily object-oriented CL code converges to:

(make-instance 'class :width width :height height)
repetitive arguments.
(apply #'make-instance 'class :key val :key2 val2 (when something (list :key3 val3)))
appending args to themake-instance form viaapply.

make-instance* abstracts these two patterns with shortcut arguments and apply forms respectively:

  • Shortcut arguments are a list of symbols that will be expanded into a list of eponymous keywords and args:
(make-instance*'class (height width):depth3);; =>;; (make-instance 'class :height height :width width :depth 3)
  • Apply form allows passing the lastapply argument without explicitly callingapply:
(make-instance*'class:width3:height5 (when three-dimentions (list:depth3)));; =>;; (apply #'make-instance 'class :width 3 :height 5 (when three-dimentions (list :depth 3)))

Both of these patterns can be used together, dramatically shortening the code:

(make-instance*'class (width height) (when three-dimentions (list:depth3)));; =>;; (apply #'make-instance 'class :width width :height height (when three-dimentions (list :depth 3)))

Note that using either of these conveniences as the solemake-instance* argument is an ambiguous case that should be avoided by providing either shortcuts or apply form as an explicit NIL/().

See themake-instance* documentation for more examples and details.

Changes fromdefclass-star

  • Renameddefclass* todefine-class (althoughdefclass* is still available as alias, alongsidedefine-class*).
  • Renameddefcondition* todefine-condition* (defcondition* is still available as alias ofdefine-condition*).
  • Added convenience macros beyond class definition:
    • define-generic for concise generic function definition (withdefgeneric* anddefine-generic* aliases).
    • make-instance* (withmake* alias) to abstract eponymous keywords and arguments and inline theapply #'make-instance idiom.
  • Default slot value when initform is omitted isnil. To leave slot unbound, specify:unbound as initform value.
  • Only the core system has been kept, the ContextL, hu.dwim.def and Swank optional features have been removed.
  • New predicate name transformersalways-dashed-predicate-name-transformer andquestion-mark-predicate-name-transformer.
  • New type inference options::automatic-types-p and:type-inference.
  • Default accessor transformer now follows the slot name. hu.dwim.defclass-star default accessor is available asdwim-accessor-name-transformer.
  • Bug fixes:
    • No longer try to exportNIL.
    • Always return the class.
    • Avoid unneededprogn.
    • Do not generate generic functions and accessors in foreign packages when:accessor-name-package is:slot-name and:accessor is not provided. (If accessor already exists in foreign package, then the new one is generated.)

Change Log

0.6.1

  • RemoveNASDF as a dependency.

0.6.0

  • Makedefine-generic declaration parsing smarter.
  • Ensure more correctdefine-generic body parsing.
    • Interpret a single-string body as method body and signal warnings due to the ambiguity of it.

0.5.0

  • Auto-generate documentation for class predicates.
  • Auto-generate documentation for slot accessors.
  • Add:export-generic-name-p option todefine-generic.

0.4.0

  • Addmake-instance* anddefine-generic convenience macros.
  • Add alias macros, likedefclass*,defcondition*,defgeneric*, andmake*.
  • Ensure documentation is always set for classes, generics, and conditions.

0.3.0

  • Default to nil when slot value is unspecified.
  • Enable accessor generation in foreign package when it already exists.
  • Bug fixes.

0.2.1

  • Fixdefault-accessor-name-transformer to follow:accessor-name-package.
  • Do not generate accessors in foreign packages when:accessor-name-package is:slot-name and:accessor is not provided.

0.2.0

  • Fixexport-predicate-name-p class option.
  • Allow type inference to check for types in superclasses.

Alternatives

defclass/std is another popular library with a similar goal, but with more insistance on conciseness, maybe at the expanse of readability. In particular, it implements a way to specify slots by properties which may seem unnatural (we read slots by their name, not by their properties).

Implementation notes

Metaclasses would not be very useful here since most of our features need to be enacted at compile-time, while metaclasses are mostly useful on classeinstances.

History

NClasses was originally developed forNyxt, so the “N” may stand for it, or “New”, or whatever poetic meaning you may find behind it!

About

A `define-class` macro for less boilerplate

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Common Lisp100.0%

[8]ページ先頭

©2009-2025 Movatter.jp