Skip to main content

Reflection

You can use standard JavaScript methods to inspect LIPS objects.

  • Scheme functions

    • dir - this procedure is inspired by Python function with the same name, it returns all properties that you can access on an object. Including those from object prototype and whole chain.
    • env - function return everything what is inside current-environment.
  • JavaScript functions

    • Object.keys - this JavaScript method will return all string keys from an object.
    • Object.values - return the value of the object.
    • Object.entries - this return array of [key,value] pairs.
    • Object.getOwnPropertySymbols - similar to Object.keys but return all symbols

Numbers

You can access internal representation of numbers as JavaScript objects

(let ((num 1/2+10i))
  (print num.__im__)
  (print num.__re__)
  (print num.__re__.__denom__)
  (print num.__re__.__num__))
;; ==> 10
;; ==> 1/2
;; ==> 2
;; ==> 1

Lists and Pairs

You can access Pairs as JavaScript objects:

(let ((x '(1 2 3)))
  x.cdr.cdr.car)
;; ==> 3

You can also manipulate the list with set!:

(let ((x '(1 2 3)))
  (set! x.cdr.cdr.cdr x)
  x)
;; ==> #0=(1 2 3 . #0#)

Above create a cycle. When you try to display a cycle it's printed using R7RS datum syntax.

Strings

Same as with numbers and list you can access internals of Strings.

(let ((str "hello"))
  (str.__string__.toUpperCase))
;; ==> "HELLO"

__string__ property is read only so you can't modify its value:

(let ((str "hello"))
  (set! str.__string__ "world")
  str)
;; ==> Cannot assign to read only property '__string__' of object '[object Object]'

Characters

Similar to string you can access internals of Characters.

(let ((x #\X)) (dir x))
;; ==> (__char__ constructor toUpperCase toLowerCase toString serialize valueOf)

the __char__ property is a string that hold the value of the character.

(let ((x #\X))
  (write x)
  (newline)
  (write x.__char__)
  (newline))
;; ==> #\X
;; ==> "X"

Procedures

As described in Core features, procedures are JavaScript functions, they also hold additional properties like __code__ and __doc__. The first property is the live source code of the procedure that you can modify:

(define (repeater x)
   "(repeater value)

    Function prints the value 1 time and modifies itself to repeat
    (+ n 1) times on the next call."
   (for-each (lambda () (print x)) (range 1))
   (let ((r (cadr (cdadddr (. repeater '__code__)))))
     (set-cdr! r (list (+ (cadr r) 1)))))
info

The __code__ property always contain a lambda expression, because:

(define (foo x) ...)

is an alias for:

(define foo (lambda (x) ...))

This procedure modify its source code. Each time you execute this function it will run one more times.

(print "1")
(repeater 'hello)
;; ==> 1
;; ==> hello
(print "2")
(repeater 'hello)
;; ==> 2
;; ==> hello
;; ==> hello
(print "3")
(repeater 'hello)
;; ==> 3
;; ==> hello
;; ==> hello
;; ==> hello

The first expression of the procedure is a doc string, unless a string is the only expression, in that case it's a return value. To access doc string you can use help or __doc__.

repeater.__doc__
"(repeater value)

Function prints the value 1 time and modifies itself to repeat
(+ n 1) times on the next call."