trivial-coerce

2022-07-08

`trivial-coerce` primarily provides a `trivial-coerce:coerce` function intended as an extensible alternative to `cl:coerce`.

Upstream URL

github.com/digikar99/trivial-coerce

Author

Shubhamkar Ayare (shubhamayare@yahoo.co.in)

License

MIT
README

trivial-coerce

Status

  • Re-implemented using polymorphic-functions.extended-types use (optimize (speed 3)) with (< debug 3) to compile-time optimize for inline static dispatch.
  • API is immature; wait for a few months or years until this library gets more thoroughly tested.

I suspect that unless there are requests, I will not work on optimizing for run-time performance.

In case someone has a better idea for trivial-coerce - feel free to raise an issue, and in the worst case, if more than a handful think (or I get convinced) the other library better deserves the name, I'd be glad to rename this library to something else. (Therefore, use with package-local-nicknames.)

Crucial dependencies:

Why

Common Lisp has a variety of non-uniform type-conversions. Some use cl:coerce, others use cl:float or cl:string, and lots more. In some sense, the cl:coerce function is information-preserving in that it prevents non-integers from being coerced into integers, or characters from being converted to and from integers. If you find these semantics useful, you might not find trivial-coerce:coerce as useful.

OTOH, a case could be made that the prevalence of information non-preserving type conversions makes things easier for prototyping, enables better polymorphism, as well as makes things easier for new people.

Examples

See coercions.lisp and tests.lisp and docstrings.

generic-cl:coerce

I do not find generic-functions suitable for the purpose of coercion. For instance:

(in-package :generic-cl)
(deftype int () 'integer)
(defmethod coerce ((num real) (type (eql 'integer)))
  (floor num))

(coerce 2.5 'integer) ;=> works
(coerce 2.5 'int) ;=> does not work
(trivial-coerce:coerce 2.5 'int) ;=> works

;; Yet another example would concern the order of applicants in a OR or AND or MEMBER
;; compound-type-specifiers - and it surely feels useful to say: (and vector (not string))

I do not know of alternatives.

Documentation

The main function is (trivial-coerce:coerce object output-type-spec): This converts OBJECT to type specified by OUTPUT-TYPE-SPEC.

The applicable coercion is guaranteed to take an object of (super)type of OBJECT and return an object of type= specified by OUTPUT-TYPE-SPEC. (See Role of Extended Types.)

Important functions and macros

  • coerce
  • list-all-coercions
  • define-coercion
  • undefine-coercion

Example usages of define-coercion can be found in coercions.lisp.

Compile Time Optimizations

CL-USER> (defun to-type (a type)
           (coerce a type))
TO-TYPE
CL-USER> (defun to-type (a type)
           (declare (optimize speed))
           (coerce a type))

; (Compiler) Macro of
;    #<POLYMORPHIC-FUNCTIONS:POLYMORPHIC-FUNCTION COERCE (38)> 
; is unable to optimize
;   (COERCE A TYPE)
; because:
;   
;   Type of
;     TYPE
;   could not be determined
WARNING: redefining COMMON-LISP-USER::TO-TYPE in DEFUN
TO-TYPE
CL-USER> (defun to-string (a)
           (declare (optimize speed))
           (coerce a 'string))
           
; (Compiler) Macro of
;    #<POLYMORPHIC-FUNCTIONS:POLYMORPHIC-FUNCTION COERCE (38)> 
; is unable to optimize
;   (COERCE A 'STRING)
; because:
;   
;   Type of
;     A
;   could not be determined
TO-STRING
CL-USER> (defun to-string (a)
           (declare (optimize speed)
                    (type number a))
           (coerce a 'string))
WARNING: redefining COMMON-LISP-USER::TO-STRING in DEFUN
TO-STRING
CL-USER> (disassemble 'to-string)
; disassembly for TO-STRING
; Size: 17 bytes. Origin: #x5374A224                          ; TO-STRING
; 24:       B902000000       MOV ECX, 2
; 29:       FF7508           PUSH QWORD PTR [RBP+8]
; 2C:       B8C2333650       MOV EAX, #x503633C2              ; #<FDEFN SB-INT:STRINGIFY-OBJECT>
; 31:       FFE0             JMP RAX
; 33:       CC10             INT3 16                          ; Invalid argument count trap
NIL

Role of Extended Types

The form (define-coercion (sequence :to list :from sequence) (cl:coerce sequence 'list)) macroexpands to:

(defpolymorph (coerce :inline t)
    ((sequence sequence) (#:output-type-spec1824 (type= list)))
    list
  (declare (ignorable sequence #:output-type-spec1824))
  (common-lisp:coerce sequence 'list))

Thus, we use the extended type (type= list) to denote all the type specifiers that are type= to list. Thus (typep '(and list) '(type= list)) holds (using polymorphic-functions.extended-types:typep).

*If you are using extensible-compound-types, then the appropriate type= would be extensible-compound-types:type= instead of polymorphic-functions.extended-types:type=.

Dependencies (4)

  • ctype
  • fiveam
  • polymorphic-functions
  • trivial-types

Dependents (1)

  • GitHub
  • Quicklisp