├── LICENSE ├── README.md ├── docstrings.lisp └── more-docstrings.asd /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 ciel-lang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Augment the docstring of built-in functions and macros with more explanations and examples. 3 | 4 | Let's see how useful it is for newcomers. 5 | 6 | This is part of [CIEL](https://github.com/ciel-lang/CIEL/) (CIEL Is an Extended Lisp). 7 | 8 | A few functions are done: 9 | 10 | - `loop`: no docstring by default. It becomes 11 | 12 | ``` 13 | The basic LOOP structure is 14 | 15 | (loop for x in (list x y z) 16 | do …) 17 | 18 | "do" is for side effects. 19 | 20 | Use "collect" to return results: 21 | 22 | (loop for x in (list 1 2 3) 23 | collect (* x 10)) 24 | 25 | To iterate over arrays, use "across" instead of "in". 26 | 27 | To iterate over hash-tables… try MAPHASH first :D 28 | 29 | For many examples, see the CL Cookbook: 30 | https://lispcookbook.github.io/cl-cookbook/iteration.html 31 | ``` 32 | 33 | - `mapcar`, `mapcan` 34 | - `maphash` 35 | - `sort` 36 | - `loop` 37 | - `defun`, `defmacro` 38 | - `defclass`, `defgeneric`, `print-object` 39 | - `defstruct` 40 | - `find` 41 | - `with-open-file` 42 | - `round` 43 | 44 | and variables: 45 | 46 | - `*default-pathname-defaults*` 47 | 48 | and that's it. To be continued. 49 | 50 | A gotcha though: 51 | 52 | - we are modifying the symbols in the :cl package, there is currently no way to "undo" these additions. 53 | 54 | ## Installation 55 | 56 | This library is on [Ultralisp](https://ultralisp.org/github). 57 | -------------------------------------------------------------------------------- /docstrings.lisp: -------------------------------------------------------------------------------- 1 | (uiop:define-package :more-docstrings 2 | (:use :cl)) 3 | 4 | (in-package :more-docstrings) 5 | 6 | #| 7 | Add more documentation and add examples to the docstrings. 8 | 9 | Now, when we read a function documentation from our editor or with the 10 | built-in `documentation` function, we can learn more about it, and see 11 | examples. We have less the need to reach for external resources. 12 | 13 | The goal is still to ease the first contact of newcomers with CL. 14 | For example, give examples on how to use MAP, MAPCAR, MAPCAN. 15 | 16 | XXX: gotchas 17 | 18 | - if we quickload :ciel twice, the docstrings 19 | are appended twice too :S The hash-table cache doesn't help in that 20 | case. 21 | - we are modifying the symbols in the :cl package, not the ones in :ciel. 22 | 23 | |# 24 | 25 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 26 | ;;; One function to do the job: 27 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 28 | 29 | (defvar *docstrings-cache* (make-hash-table) 30 | "Cache the original docstring of functions and macros we are augmenting. 31 | Mainly to ease our tests at the REPL.") 32 | 33 | (defun documentation-with-cache (symbol &optional (doc-type 'function)) 34 | (let ((cached (gethash symbol *docstrings-cache*))) 35 | (if cached 36 | cached 37 | 38 | (let ((doc (documentation symbol doc-type))) 39 | (setf (gethash symbol *docstrings-cache*) 40 | ;; don't store a NIL docstring. 41 | (or doc "")) 42 | doc)))) 43 | 44 | (defun docstring-append (symbol s &optional (doc-type 'function)) 45 | "Add S to the docstring of SYMBOL (to designate a function or a macro). 46 | DOC-TYPE is the required argument of DOCUMENTATION, by default 'function (for functions and macros), otherwise use 'variable." 47 | (let ((doc (documentation-with-cache symbol doc-type))) 48 | (setf (documentation symbol doc-type) 49 | (str:concat doc s)))) 50 | 51 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 52 | ;;; Now use it. 53 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 54 | 55 | ;;; Variables 56 | 57 | (docstring-append '*default-pathname-defaults* " 58 | 59 | An implementation-dependent pathname, typically in the working directory that was current when Common Lisp was started up. 60 | 61 | Read more: 62 | 63 | - https://cl-community-spec.github.io/pages/002adefault_002dpathname_002ddefaults_002a.html" 64 | 'variable) 65 | 66 | ;;; mapcar 67 | (docstring-append 'mapcar " 68 | 69 | For example: 70 | 71 | (mapcar #'+ '(1 2 3) '(10 20 30)) ;; => (11 22 33) 72 | 73 | (mapcar (lambda (x) 74 | (format t \"~a is ~R~&\" x x)) 75 | '(1 2 3)) 76 | ;; => 77 | 1 is one 78 | 2 is two 79 | 3 is three 80 | (NIL NIL NIL) 81 | ") 82 | 83 | 84 | ;;; mapcan 85 | (docstring-append 'mapcan " 86 | 87 | NCONC concatenates lists destructively.") 88 | 89 | 90 | ;;; sort 91 | (docstring-append 'sort " 92 | 93 | Since SORT is destructive, use COPY-LIST: 94 | 95 | (setq mylist (list 1 3 2)) 96 | (sort (copy-list mylist) #'<) 97 | 98 | See also STABLE-SORT.") 99 | 100 | 101 | ;;; loop 102 | (docstring-append 'loop " 103 | The basic LOOP structure is 104 | 105 | (loop for x in (list x y z) 106 | do …) 107 | 108 | \"do\" is for side effects. 109 | 110 | Use \"collect\" to return results: 111 | 112 | (loop for x in (list 1 2 3) 113 | collect (* x 10)) 114 | 115 | To iterate over arrays, use \"across\" instead of \"in\". 116 | 117 | To iterate over hash-tables… try MAPHASH first :D 118 | 119 | For many examples, see the CL Cookbook: 120 | https://lispcookbook.github.io/cl-cookbook/iteration.html") 121 | 122 | 123 | ;;; maphash 124 | (docstring-append 'maphash " 125 | 126 | Example: 127 | 128 | (maphash (lambda (key value) 129 | (format t \"key is: ~a, value is: ~a~&\" key value)) 130 | (dict :a 'one)) 131 | ;; => key is: A, value is: ONE") 132 | 133 | 134 | ;;; defun 135 | (docstring-append 'defun " 136 | 137 | Example: 138 | 139 | (defun hello (name) 140 | \"Say \\\"hello\\\" to NAME.\" 141 | (format t \"Hello ~a!\" name)) 142 | 143 | Define named parameters with &key: 144 | 145 | (defun hello (name &key lisper) 146 | ...) 147 | 148 | and use it like so: (hello \"you\" :lisper t) 149 | 150 | Key parameters are NIL by default. Give them another default value like this: 151 | 152 | (defun hello (name &key (lisper t)) 153 | ...) 154 | 155 | Read more: 156 | https://gigamonkeys.com/book/functions.html 157 | https://lispcookbook.github.io/cl-cookbook/functions.html") 158 | 159 | 160 | ;;; defmacro 161 | (docstring-append 'defmacro "Macros operate on code, which they see as lists of lists of symbols. 162 | 163 | Macros, unlike functions, do not evaluate their arguments. They 164 | expand (at compile time) into another piece of code, that will 165 | eventually be evaluated. 166 | 167 | First rule for macros: don't write a macro when a function can do. 168 | 169 | Example macros: DEFUN LOOP SETF WITH-OPEN-FILE 170 | 171 | See also: QUOTE BACKQUOTE GENSYM MACROEXPAND 172 | 173 | Read more: 174 | https://lispcookbook.github.io/cl-cookbook/macros.html 175 | https://gigamonkeys.com/book/macros-standard-control-constructs.html 176 | https://www.youtube.com/watch?v=ygKXeLKhiTI Little bits of Lisp video 177 | ") 178 | 179 | ;;; defclass 180 | (docstring-append 'defclass "The macro defclass defines a new named class. It returns the new class object as its result. 181 | 182 | Example: 183 | 184 | (defclass living-being () ()) 185 | 186 | (defclass person (living-being) 187 | ((name 188 | :initarg :name 189 | :initform \"\" 190 | :accessor name) 191 | (lisper 192 | :initarg :lisper 193 | :initform nil 194 | :accessor lisper 195 | :documentation \"Set to non-nil if this person fancies Lisp.\"))) 196 | 197 | Slots are unbound by default, here we prefer them to be the empty string and nil. 198 | 199 | An :accessor creates a generic method. You can have the same accessor name in different classes. 200 | 201 | Create an instance of that class with MAKE-INSTANCE: 202 | 203 | (make-instance 'person :name \"Alice\" :lisper t) 204 | 205 | Define how to pretty-print an object with PRINT-OBJECT. 206 | 207 | After we change a class definition (slots are modified, added or removed), we can control how an object is updated with UPDATE-INSTANCE-FOR-REDEFINED-CLASS. 208 | 209 | Read more: 210 | https://lispcookbook.github.io/cl-cookbook/clos.html 211 | https://cl-community-spec.github.io/pages/defclass.html 212 | ") 213 | 214 | ;;; to be continued. 215 | 216 | (docstring-append 'print-object "The generic function print-object writes the printed representation of object to stream. The function print-object is called by the Lisp printer; it should not be called by the user. 217 | 218 | Example: 219 | 220 | (defmethod print-object ((obj person) stream) 221 | (print-unreadable-object (obj stream :type t :identity t) 222 | (with-slots (name lisper) obj 223 | (format stream \"~a, lisper: ~a\" name lisper)))) 224 | 225 | (make-instance 'person :name \"Alice\") 226 | ;; => 227 | # 228 | (1) (2) (3) 229 | 1 tells the reader that this object can't be read back in 230 | 2 is the object type 231 | 3 is the object identity (address). 232 | 233 | Read more: 234 | https://cl-community-spec.github.io/pages/print_002dobject.html 235 | https://lispcookbook.github.io/cl-cookbook/clos.html#pretty-printing 236 | ") 237 | 238 | (docstring-append 'defstruct " 239 | 240 | Example: 241 | 242 | (defstruct person 243 | name age) 244 | 245 | Creates the `make-person' constructor function, the `person-p' predicate as well as the `person-name' and `person-age' setf-able functions: 246 | 247 | (person-name (make-person :name \"lisper\")) 248 | ;; => \"lisper\" 249 | 250 | Read more: 251 | 252 | - https://lispcookbook.github.io/cl-cookbook/data-structures.html#structures 253 | - https://cl-community-spec.github.io/pages/defstruct.html") 254 | 255 | (docstring-append 'defgeneric " 256 | 257 | A generic function is a lisp function which is associated 258 | with a set of methods and dispatches them when it's invoked. All 259 | the methods with the same function name belong to the same generic 260 | function. 261 | 262 | The `defgeneric` form defines the generic function. If we write a 263 | `defmethod` without a corresponding `defgeneric`, a generic function 264 | is automatically created. 265 | 266 | Example: 267 | 268 | (defgeneric greet (obj) 269 | (:documentation \"says hi\") 270 | (:method (obj) 271 | (format t \"Hi\"))) 272 | 273 | ") 274 | 275 | (docstring-append 'find " 276 | Search for ITEM in SEQUENCE, return ITEM. 277 | 278 | Example: 279 | 280 | (find 20 '(10 20 30)) ;; => 20 281 | (find \"foo\" '(\"abc\" \"foo\") :test #'string-equal) ;; => \"foo\" 282 | 283 | See also: `find-if', `position', `search', `index', `elt'… 284 | 285 | Read more: 286 | 287 | - https://cl-community-spec.github.io/pages/find.html 288 | - https://lispcookbook.github.io/cl-cookbook/data-structures.html") 289 | 290 | (docstring-append 'with-open-file " 291 | Example: 292 | 293 | write to a file: 294 | 295 | (with-open-file (f \"/path/to/file.txt\" :direction :output 296 | :if-exists :supersede 297 | :if-does-not-exist :create) 298 | (write-sequence \"hello file\" f)) 299 | 300 | This binds a stream to the `f' variable and we write content to it. 301 | 302 | You can read files with :direction :input as well as UIOP: uiop:read-file-string, uiop:read-file-lines etc. 303 | 304 | Read more: 305 | 306 | - https://lispcookbook.github.io/cl-cookbook/files.html 307 | - https://cl-community-spec.github.io/pages/with_002dopen_002dfile.html 308 | ") 309 | 310 | (docstring-append 'round " 311 | 312 | See also: 313 | 314 | - `fround', that returns the rounded value as a float 315 | - `ceiling', `floor' and `truncate' (and their f… equivalent). 316 | 317 | Read more: 318 | 319 | - https://lispcookbook.github.io/cl-cookbook/numbers.html 320 | - https://cl-community-spec.github.io/pages/floor.html") 321 | 322 | #+ciel 323 | (docstring-append 'function-cache:defcached " 324 | 325 | Example: 326 | 327 | (defcached (foo :timeout 10) (arg) 328 | (sleep 3) 329 | arg) 330 | 331 | The functions's result is cached for 10 seconds, for the given argument. A second call returns immediately.") 332 | -------------------------------------------------------------------------------- /more-docstrings.asd: -------------------------------------------------------------------------------- 1 | (in-package :cl-user) 2 | 3 | ;; This .asd definition is not used in CIEL, where docstring.lisp is 4 | ;; included in the sources, so we just load the .lisp file. 5 | ;; 6 | ;; It is there to give it a chance to be used outside of CIEL. We'll see. 7 | (asdf:defsystem "more-docstrings" 8 | :version "0.1" 9 | :author "vindarel" 10 | :license "MIT" 11 | :homepage "https://github.com/ciel-lang/more-docstrings/" 12 | :depends-on ("str") 13 | :components ((:file "docstrings")) 14 | 15 | :description "more-docstrings augments the docstring of built-in functions, macros and variables to give more explanations and examples. It is part of the CIEL project.") 16 | --------------------------------------------------------------------------------