Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Topics - hartrock

#21
Currently there is the following for guessing, which could be the script currently being executed:

(context sys)
;;
;; A (scriptpath), (scriptname), (scriptargs) solution for skipping newlisp opts
;; and their args: could be a helper for getopts.
;;
;; Should be correct for typical shebang (#!/...) cases, but of interest here
;; are newlisp calls like:
;;   newlisp -s 4096 -m 10 someScript.lsp
;; .
;;
;; But it has limitations: it is only correkt, if *first* non-option arg of
;; newlisp is the script of interest.
;; E.g. calling
;;   newlisp -m 10 nonExistentFile
;; results into
;;   > (sys:scriptname)
;;   "nonExistentFile"
;; .
;; Therefrom it should be allowed and documented how to override; this can be
;; done by setting scriptpath_ix explicitely, in case of used heuristics fails.
;;
;; See file:///usr/share/doc/newlisp/newlisp_manual.html#options:
;;
;;  -h this help                   -> OK (enters interpreter)
;;  -n no init.lsp (must be first) -> OK
;;  -x <source> <target> link      -> error: should not been reached by script
;;  -v version                     -> OK (enters interpreter)
;;  -s <stacksize>                 -> OK
;;  -m <max-mem-MB> cell memory    -> OK
;;  -e <quoted lisp expression>    -> OK (enters interpreter)
;;  -l <path-file> log connections -> OK
;;  -L <path-file> log all         -> OK
;;  -w <working dir>               -> OK
;;  -c no prompts, HTTP            -> OK
;;  -C force prompts               -> OK
;;  -t <usec-server-timeout>       -> OK
;;  -p <port-no>                   -> OK
;;  -d <port-no> demon mode        -> OK
;;  -http only                     -> OK
;;  -6 IPv6 mode                   -> OK
;;
(set'opt_without_arg
 '("-h" ; enters interpreter
   "-n" ; -> skipped
   "-v" ; enters interpreter
   "-c" ; -> skipped
   "-C" ; -> skipped
   "-http" ; -> skipped
   "-6" ; -> skipped
   )
 'opt_with_arg
 '("-s" ; -> skipped
   "-e" ; enters interpreter
   "-m" ; -> skipped
   "-l" ; -> skipped
   "-L" ; -> skipped
   "-w" ; -> skipped
   "-t" ; -> skipped
   "-p" ; -> skipped
   "-d" ; -> skipped
   )
 'opt_with_2_args
 '("-x" ; should not been reached by script
   ;;"-y" ; for testing errorcase...
   ))
(local (breakFlag skip_next ix execPath)
  (set 'ix 0) ; without any args ix 0 refers to newlisp bin
  (dolist (o (1 (main-args)) breakFlag) ; without args, there is no loop here
   (cond
    (skip_next
     (++ ix)
     (set 'skip_next nil)) ; skip once
    ((find o opt_without_arg)
     (++ ix))
    ((find o opt_with_arg)
     (++ ix)
     (set 'skip_next true))
    ((find o opt_with_2_args)
     (throw-error "should not been reached"))
    ("default" ; end loop: first newlisp noopt should be script
     (++ ix) ; needed: loop started with ix of previous element
     (set 'breakFlag true))))
  (set 'scriptpath_ix ix)) ; 0 or index of first element not being a newlisp option with its args
;; iface
(define (scriptpath-ix) ; needed for getopts below
  scriptpath_ix)
(define (scriptargs) ; good as getopts arg
  ((++ (scriptpath-ix)) (main-args)))
(define (scriptpath)
  (main-args scriptpath_ix))
(define (scriptname) ; Linux (to be extended for other OSes)
  (last (parse (scriptpath) "/")))

This approach replicates interpreter code and has serious limitations, because it is unknown, which given script is currently being executed after its interpreter load, if there are multiple args, which all could be scripts: e.g.

- newlisp arg_1.lsp arg_2.lsp arg_3.lsp [options][/list]

could mean

- newlisp lib_1.lsp lib_2.lsp scriptOfInterest.lsp [options]

or

- newlisp lib_1.lsp scriptOfInterest.lsp scriptArg.lsp [options]

.

What about having a way to get the (main-args) index of last interpreter loaded and currently executed script?

This would allow to get the best possible scriptname for more complicate cases than just having only one script directly after newlisp opts, which is helpful for a most general getopts module.

It would also allow to do

- newlisp scriptOfInterest_1.lsp scriptOfInterest_2.lsp

and give different (scriptname)s - for e.g. err messages - then (without setting them explicitely by the scripts themselves).
#22
cpop stands for 'choice' pop, where the cpop is able to choose, if a standard push without ix leads to FIFO semantics on cpops side.



Currently only the push has this choice of switching from standard LIFO to FIFO semantics, by pushing back using -1 ix, which is working for an empty list: on the other side pop fails, if poping from an empty list with -1 ix.



Here are two macros implementing cpop semantics:

;; best in functionality
(context 'cpop)
(define-macro (cpop:cpop l (ix 0))
  (if (not (empty? (eval l)))
      (pop (eval l) (eval ix)))) ; returns nil, if empty list
(context MAIN)

;; this has limitations, but should be faster
(macro (emacro-cpop L (Ix 0)) ; emacro: expansion macro
  (if (not (empty? L))
      (pop L Ix))) ; returns nil, if empty list

After loading former code, there has been the following session to show its functionality:

> ;;
> ;; choices by push for standard pop
> ;;
> ; LIFO
> (push 3 (push 2 (push 1 l)))    ; standard push leading to LIFO
(3 2 1)
> (pop l) (pop l) (pop l) (pop l) ; standard pop
3
2
1
nil
> ;
> ; FIFO:
> (push 3 (push 2 (push 1 l -1) -1) -1) ; FIFO push
(1 2 3)
> (pop l) (pop l) (pop l) (pop l)       ; standard pop
1
2
3
nil
> ;;
> ;;
> ;; choices by pop for standard push
> ;;
> ;; LIFO choice by pop here is the same as LIFO choice by push for standard pop above:
> ;; both with standard push'n'pop.
> ;
> ; FIFO fails, if the list has become empty:
> (push 3 (push 2 (push 1 l)))                ; standard push
(3 2 1)
> (pop l -1) (pop l -1) (pop l -1) (pop l -1) ; FIFO pop (failing)
1
2
3

ERR: invalid list index in function pop
> ; -> this is the problem
> ;
> ; FIFO choice by cpop works:
> (push 3 (push 2 (push 1 l)))                    ; standard push
(3 2 1)
> (cpop l -1) (cpop l -1) (cpop l -1) (cpop l -1) ; FIFO cpop (working)
1
2
3
nil
>


A difference between expansion and run-time macro:

> ;; this shows a difference between the different macro types: expansion macro works in many cases:
> (push 3 (push 2 (push 1 l)))
(3 2 1)
> (em-cpop l -1) (em-cpop l -1) (em-cpop l -1) (em-cpop l -1) ; (working)
1
2
3
nil
> ;;
> ; but not in all:
> (push 3 (push 2 (push 1 l)))
(3 2 1)
> ((if true cpop "dummy") l -1)    ; -> run-time macro works
1
> ((if true em-cpop "dummy") l -1) ; -> expansion macro fails
(if (not (empty? l))
 (pop l -1))
>


Extending pop by building in cpops FIFO semantics would only be a minor change in interpreter code: it only needs the addition of an empty? list check (so performance should be no problem).
#23
[Update 2] Feature request ([fr]) cancelled.

There is a reason for not making (pop aList -1) as simple as (push elem aList -1): it is performance, which degrades for longer lists, because there is a pointer forwards from each cell to its next cell in the list, but not backwards. This has implications for pop back (because its predecessor has to be reached from the other list end), but not for push back: thanks to 'last element optimization' of keeping a pointer to last element in list.

So there is an asymmetry in the sense, that regarding performance it's better to leave the FIFO/LIFO choice at pushers side, which is visible in making (or leaving) the less performant variants more difficult to use.

Nevertheless there may be usecases for the cpop/em-cpop macros (see link in previous update).



[Update] See http://www.newlispfanclub.alh.net/forum/viewtopic.php?f=16&t=4732#p23319">//http://www.newlispfanclub.alh.net/forum/viewtopic.php?f=16&t=4732#p23319 for better and more general macro code.




  • [*] On one side push is robust, if pushing into s symbol set to nil: it creates an empty list then, and ignores some given index just in this special case.

  • [*] On the other side pop always expects a list, no one will be created, if there is none (together with ignoring a given ix then).
  • [/list]
    But this is not the problem for FIFO functionality: this would be solved by some more symmetrie between push and pop at another place.




     
  • [*] (pop '() -1)) and (pop '() 0)

        as valid expression returning nil; this corresponds to

  •  
  • [*] (push "foo" '() 0) and (push "foo" '() -1)

        , which is working now.
  • [/list]
  • [*] More more symmetry would be reached by creating an empty list for pop'ing from a nil symbol value, just as for push (ignoring any given ix and returning nil in this case).
  • [/list]
    Interesting to note, that
    (push '() 1)
    does not work; pushing to an empty list only works for indices 0 and -1. It could be better for detecting coding errors, if the indices would be limited to 0 and -1 as in the nil symbol value case (should be the same for pop after 'more more symmetry' then).





    It is clear, that changing the semantics of push and/or pop could break some legacy code; so such a change would likely deserve a greater version number increase.




    ;; define some things
    (macro (push-top    E L) (push E L  ))
    (macro (push-bottom E L) (push E L -1))
    (macro (pop-top     L) (pop L  ))
    (macro (pop-bottom  L) (pop L -1))
    ;; repair by:
    (macro (pop-bottom-repaired L) (if (> (length L) 0) (pop L -1)))

    (define (doIt num_push push_op_sym num_pop pop_op_sym)
      (print (format "%45s" (string "push_op: " push_op_sym ", pop_op: " pop_op_sym)))
      (set 'l '()
           'push_op (eval push_op_sym)
           'pop_op (eval pop_op_sym))
      (dolist (e (sequence 1 num_push))
              (eval (push_op e l)))
      (print "; after push_op: " l)
      (print "; pop:")
      (dotimes (i num_pop)
               (print " " (eval (pop_op l))))
      (println))
    [/code]

    There has been the following session:

    newLISP v.10.6.4 64-bit on OSX IPv4/6 UTF-8 libffi, options: newlisp -h

    > ;;
    > ;; this works:
    > ;;
    > (doIt 3 'push-top 3 'pop-top) ; LIFO
               push_op: push-top, pop_op: pop-top; after push_op: (3 2 1); pop: 3 2 1
    nil
    > (doIt 3 'push-bottom 3 'pop-bottom) ; LIFO
         push_op: push-bottom, pop_op: pop-bottom; after push_op: (1 2 3); pop: 3 2 1
    nil
    > (doIt 3 'push-bottom 3 'pop-top) ; FIFO
            push_op: push-bottom, pop_op: pop-top; after push_op: (1 2 3); pop: 1 2 3
    nil
    > (doIt 3 'push-top 3 'pop-bottom) ; FIFO
            push_op: push-top, pop_op: pop-bottom; after push_op: (3 2 1); pop: 1 2 3
    nil
    > ;;
    > ;; but this fails, if 'pop-bottom is trying to pop from an empty list:
    > ;;
    > (doIt 3 'push-top 4 'pop-top) ; LIFO
               push_op: push-top, pop_op: pop-top; after push_op: (3 2 1); pop: 3 2 1 nil
    nil
    > (doIt 3 'push-bottom 4 'pop-bottom) ; LIFO
         push_op: push-bottom, pop_op: pop-bottom; after push_op: (1 2 3); pop: 3 2 1
    ERR: invalid list index in function pop
    called from user function (doIt 3 'push-bottom 4 'pop-bottom)
    > (doIt 3 'push-bottom 4 'pop-top) ; FIFO
            push_op: push-bottom, pop_op: pop-top; after push_op: (1 2 3); pop: 1 2 3 nil
    nil
    > (doIt 3 'push-top 4 'pop-bottom) ; FIFO
            push_op: push-top, pop_op: pop-bottom; after push_op: (3 2 1); pop: 1 2 3
    ERR: invalid list index in function pop
    called from user function (doIt 3 'push-top 4 'pop-bottom)
    > ;;
    > ;; using repaired version: voila!
    > ;;
    > (doIt 3 'push-top 4 'pop-top) ; LIFO
               push_op: push-top, pop_op: pop-top; after push_op: (3 2 1); pop: 3 2 1 nil
    nil
    > (doIt 3 'push-bottom 4 'pop-bottom-repaired) ; LIFO
    push_op: push-bottom, pop_op: pop-bottom-repaired; after push_op: (1 2 3); pop: 3 2 1 nil
    nil
    > (doIt 3 'push-bottom 4 'pop-top) ; FIFO
            push_op: push-bottom, pop_op: pop-top; after push_op: (1 2 3); pop: 1 2 3 nil
    nil
    > (doIt 3 'push-top 4 'pop-bottom-repaired) ; FIFO
    push_op: push-top, pop_op: pop-bottom-repaired; after push_op: (3 2 1); pop: 1 2 3 nil
    nil
    >


    Triggered by this code (for copy/paste at once):

    ;;
    ;; this works:
    ;;
    (doIt 3 'push-top 3 'pop-top) ; LIFO
    (doIt 3 'push-bottom 3 'pop-bottom) ; LIFO
    (doIt 3 'push-bottom 3 'pop-top) ; FIFO
    (doIt 3 'push-top 3 'pop-bottom) ; FIFO
    ;;
    ;; but this fails, if 'pop-bottom is trying to pop from an empty list:
    ;;
    (doIt 3 'push-top 4 'pop-top) ; LIFO
    (doIt 3 'push-bottom 4 'pop-bottom) ; LIFO
    (doIt 3 'push-bottom 4 'pop-top) ; FIFO
    (doIt 3 'push-top 4 'pop-bottom) ; FIFO
    ;;
    ;; using repaired version: voila!
    ;;
    (doIt 3 'push-top 4 'pop-top) ; LIFO
    (doIt 3 'push-bottom 4 'pop-bottom-repaired) ; LIFO
    (doIt 3 'push-bottom 4 'pop-top) ; FIFO
    (doIt 3 'push-top 4 'pop-bottom-repaired) ; FIFO
    #24
    A GET resulting into a reply with Content-Length 0 should be possible: from

      http://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#message.body">http://svn.tools.ietf.org/svn/wg/httpbi ... ssage.body">http://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#message.body

    :
    Quote
    The presence of a message body in a request is signaled by a Content-Length or Transfer-Encoding header field. ...



    ... All 1xx (Informational), 204 (No Content), and 304 (Not Modified) responses do not include a message body. All other responses do include a message body, although the body might be of zero length.

    So this applies to e.g. '200 OK' and  '404 Not Found' responses, which may have a zero message body.



    newLISP:

    sr@mad:~$ newlisp
    newLISP v.10.6.2 64-bit on Linux IPv4/6 UTF-8 libffi, options: newlisp -h

    > (get-url "http://localhost/empty.txt" "debug")
    GET /empty.txt HTTP/1.1
    Host: localhost
    User-Agent: newLISP v10602
    Connection: close

    HTTP/1.1 200 OK

    "ERR: HTTP document empty"
    > ; but:
    > (get-url "file:///tmp/empty.txt" "debug")
    ""


    curl:

    sr@mad:~$ curl -v -X GET http://localhost/empty.txt
    * About to connect() to localhost port 80 (#0)
    *   Trying ::1...
    * connected
    * Connected to localhost (::1) port 80 (#0)
    > GET /empty.txt HTTP/1.1
    > User-Agent: curl/7.26.0
    > Host: localhost
    > Accept: */*
    >
    * additional stuff not fine transfer.c:1037: 0 0
    * HTTP 1.1 or later with persistent connection, pipelining supported
    < HTTP/1.1 200 OK
    < Date: Fri, 10 Jul 2015 12:49:10 GMT
    < Server: Apache/2.2.22 (Debian)
    < Last-Modified: Fri, 10 Jul 2015 12:10:49 GMT
    < ETag: "8075b-0-51a84433bdbcf"
    < Accept-Ranges: bytes
    < Content-Length: 0
    < Vary: Accept-Encoding
    < Content-Type: text/plain
    <
    * Connection #0 to host localhost left intact
    * Closing connection #0

    (empty message body).



    Ideas:

    [*] (http-error) for more fine-granular checking of http responses?
  • [*] net-error '24 HTTP document empty' only (possibly with 'empty' -> 'missing'), if there is no 'Content-Length or Transfer-Encoding header' for a response with an HTTP error code needing a message body?
  • [/list]
    #25

    sr@mad:~$ newlisp
    newLISP v.10.6.2 64-bit on Linux IPv4/6 UTF-8 libffi, options: newlisp -h

    > (new Tree 'hm)
    hm
    > (hm "foo" (+ (or $it 0) 1))
    1
    > (++ (hm "foo"))
    2
    > ; OK, but:
    > (++ (hm "bar"))

    ERR: invalid parameter in function ++
    >

    Is this a bug, or is there a reason for this behavior?



    It would be nice, if checking for nil could be avoided here.
    #26
    [Update 2014-01-02] links: emscripten, Resig's blog entry to asm.js.



    Beginning of new year and after doing things with newLISP for a while, it is a good time to say thanks to Lutz for:
    [*] publishing newLISP under a community oriented open source license;
  • [*] being open-minded regarding suggestions for improvements (this is not a matter of course after working with a self designed language and developing it further for such a long time);

  • [*] being communicative with giving comments to many topics: for a better understanding, why some things are as they are (which helps to learn and to avoid unsuited paths).
  • [/list]

    I've seen many programming language following different paradigms so far; amongst them are Pascal, Lisp (a classical variant sitting at terminals at a computer science department), C (near to the metal), C++ (the former + OOP with metal), Smalltalk (a very nice language for doing OOP consequently without thinking about the metal (view of Smalltalk programmers, VM implementors always (there may be rare exceptions) have to deal with the metal)), F-Logic (logical programming paradigm).



    Thinking about writing a C interpreter for Kernel http://web.cs.wpi.edu/~jshutt/kernel.html">http://web.cs.wpi.edu/~jshutt/kernel.html, I've started working through a thick book about garbage collection (because such a project would need a garbage collector). Now I've stumbled about a new language just avoiding this big can of worms: it's very interesting to me, what's possible without GC and just ORO rules.



    newLISP gives me (the unexpected) motivation to learn a new language. Under the things I like are:

    [*] Clearly functional, but without being dogmatically functional (destructive functions where it makes sense).
  • [*] Very good docs.

  • [*] Very fast reaction with fixes to bug reports.

  • [*] Let the OS make for which it is made for: e.g.

       
    [*] -> light weight processes instead of light weight threads.
  • [/list]

  • [*] The feeling, that there is a good sense for balancing different features to get high expressiveness without feature bloat (which would be difficult to be fully grasped).

  • [*] The capability of storing the state of a started newLISP system: this reminds me on Smalltalk images; possibly this feature could be used for a similar IDE functionality like in Smalltalk systems.

  • [*] System composition by starting newLISP and loading modules (libs): this is good for avoiding code bloat (in Smalltalk systems there is often the need to strip a very big development system (which can be hard)).
  • [/list]

    One newLISP thing, I've stumbled over recently.

    To me the following makes sense (this said with having some but without having much experience with newLISP):
    [*] To prefer expanding of nested lambdas against proliferation of newly generated symbols with lambdas bound to them (closure style, see http://www.newlispfanclub.alh.net/forum/viewtopic.php?f=15&t=4400&hilit=uuid">http://www.newlispfanclub.alh.net/forum/viewtopic.php?f=15&t=4400&hilit=uuid). I think this is in line with the philosophy of the language.

    I assume, the latter could lead to symbols bound to lambdas hanging around not being GCed (because there is none). On the other side there is the possibility to automatically generate names for symbols bound to lambdas, if it is really needed (due to the flexibility of the language): but then you should know why you are deviating from the default (philosophy of the language), and do it explicitely.[/list]


    At last an interesting thing outside, but related to porting newLISP.

    There is emscripten:

      https://github.com/kripken/emscripten/wiki">https://github.com/kripken/emscripten/wiki

    ; and here is asm.js:

      http://ejohn.org/blog/asmjs-javascript-compile-target/">http://ejohn.org/blog/asmjs-javascript-compile-target/

      https://hacks.mozilla.org/2013/12/gap-between-asm-js-and-native-performance-gets-even-narrower-with-float32-optimizations/">https://hacks.mozilla.org/2013/12/gap-b ... mizations/">https://hacks.mozilla.org/2013/12/gap-between-asm-js-and-native-performance-gets-even-narrower-with-float32-optimizations/

      http://asmjs.org/spec/latest/">http://asmjs.org/spec/latest/

    .

    This could lead to some newLISP interpreter running inside the browser, without too much porting effort.

    Not to ignore the issues: good Javascript and DOM bindings are laborious (these and a newLISP interpreter written directly in Javascript could be an interesting project on its own, too).
    #27
    [2. Update 2014-01-02]: it has a stable ordering (see Lutz' reply).

    [1. Update 2014-01-02]: added 'stable' to title (see replies below).



    Example:

    > (setq l '( ("0" 3) ("1" 3) ("2" 3) ("3" 3) ("4" 3)  ("foo" 2) ("bar" 4) ("foo" 1) ("bar" 5)  ("5" 3) ("6" 3) ("7" 3) ("8" 3) ("9" 3)))
    (("0" 3) ("1" 3) ("2" 3) ("3" 3) ("4" 3) ("foo" 2) ("bar" 4) ("foo" 1) ("bar" 5) ("5" 3) ("6" 3) ("7" 3) ("8" 3) ("9" 3))
    > (sort (copy l) (fn (x y) (<= (last x) (last y))))
    (("foo" 1) ("foo" 2) ("0" 3) ("1" 3) ("2" 3) ("3" 3) ("4" 3) ("5" 3) ("6" 3) ("7" 3) ("8" 3) ("9" 3) ("bar" 4) ("bar" 5))
    > (sort (copy l) (fn (x y) (< (last x) (last y))))
    (("foo" 1) ("foo" 2) ("9" 3) ("8" 3) ("7" 3) ("6" 3) ("5" 3) ("4" 3) ("3" 3) ("2" 3) ("1" 3) ("0" 3) ("bar" 4) ("bar" 5))
    > ;;
    > (= (sort (copy l) (fn (x y) (< (last x) (last y)))) (reverse (sort (copy l) (fn (x y) (>= (last x) (last y))))))
    true
    > (= (sort (copy l) (fn (x y) (> (last x) (last y)))) (reverse (sort (copy l) (fn (x y) (<= (last x) (last y))))))
    true


    Observations:

    Sorting with

    - '<=' or '>=' does not change the order of elements compared '=';

    - '<=' results into reversed elements of sorting with '>', and

    - '>=' resutls into reversed elements of sorting with '<' .



    Is this behavior guaranteed?

    In the (sort) section of the manual there is no statement regarding the '=' case.

    Sorting behavior of elements compared equal could be different without violating their sorting relation '<', '<=', '>' or '>=': in principle the ordering amongst elements compared '=' could be *arbitrary* after sorting.



    Motivation:

    I've started sorting a similar list with '<' and handled '=' elements specifically, because their order had changed compared with the original list (without being surprised about this effect). After that I've seen, that sorting with '<=' does exactly what I wanted (keeping original ordering of elements compared '='), without specific handling of the '=' case.

    Question remains, if this can be exploited or is just an implementation incident (which may change in a later newLISP version).
    #28
    Regarding refs, lists and arrays behave similar, but not the same.

    There seems to be a possibility for further unification.



    This works for both:

    (setq l (sequence 1 10)
          a (array 10 l))
    ;;
    ;; Applying refs works for both lists and arrays:
    (setq r (ref 5 l))
    (l r)
    (a r)

    But getting a ref does only work for lists (see above); this does not work:

    > (ref 5 a)

    ERR: list expected in function ref : (1 2 3 4 5 6 7 8 9 10)


    What about having refs for arrays, too?



    Then this would work, too:

    (setq r (ref 5 a))
    (l r)
    (a r)


    The semantics of an array ref could/should be the same as for list refs.



    Such unification would be good for functions operating on containers - being agnostic against the array/list difference.



    Because there already is setf for arrays, I'm assuming that most machinery to get this done already exists.
    #29
    This should work under most (all?) Linux systems:

    (setq tput_cols (exec "tput cols"))
    (if tput_cols (pretty-print (- (int (tput_cols 0)) 11)))
    ;;
    ;; then try:
    (symbols)
    #30
    [update] -> 10.5.6.



    During writing some code to ease navigation in lists, I've stumbled about something. Let's speak code for itself.



    Assumed there is a definition for ref-parent and some example list:



    ;; nil for non-existing parent

    (define (ref-parent r)
      (if (null? r) nil (0 -1 r)))
    ;;
    (setq l_nested '("1" "2" "3"
                      ("4" "5" "6"
                       ("7" "8" "9"))
                     "10" "11" "12"
                      ("13" "14" "15"
                       ("16" "17" "18")))
          )

    Now it's possible to eval:

    > (l_nested (ref-parent (ref "14" l_nested)))
    ("13" "14" "15" ("16" "17" "18"))

    ; but it is not possible to eval:

    > (l_nested (ref-parent (ref "11" l_nested)))

    ERR: missing argument

    Reason is, that:

    > (ref-parent (ref "14" l_nested))
    (7)

    ; but:

    > (ref-parent (ref "11" l_nested))
    ()

    : here '() is not being accepted as deref argument.



    What about introducing '() as allowed deref argument, which dereferences list itself to which it is applied to?

    Then the following would work:

    > (l_nested (ref-parent (ref "11" l_nested)))
    ("1" "2" "3" ("4" "5" "6" ("7" "8" "9")) "10" "11" "12" ("13" "14" "15" ("16" "17" "18")))


    This seems to be a neat unification regarding dereferencing lists by refs.



    Because ref'ing a non-existing element gives:

    > (ref "not there" l_nested)
    nil

    ; '() seems to be free for this purpose.

    If there is:

    > (ref-all "not there" l_nested)
    ()

    , we get a '(). But this is no problem, because for dereferencing refs gotten this way, we have to iterate through this list, which would do nothing here.



    Are there any problems with this proposal, which I don't see?



    What do you think?



    Feedback to this proposal is appreciated (as usual).
    #31
    Anything else we might add? / apply for arrays?
    November 16, 2013, 02:33:15 PM
    [update] -> 10.5.6.



    (setq a (array 10 (sequence 1 10)))

    ;; currently we have:
    (setq a_applied (apply + (array-list a)))

    ;; nice would be:
    (setq a_applied (apply-array + a))
    ;; or:
    (setq a_applied (apply + a))

    Func apply-array could work at source array directly (without indirection by conversions to/from lists).

    Because
     

    [*] it shouldn't be more expensive than its list counterpart,
  • [*] the result type depends on to be applied function and not on source to be applied to (array);
  • [/list]

    it probably could be named the same (and internally just do the right thing).
    #32
    newLISP in the real world / [bug[fix]] (round 0.5) -> 0
    November 15, 2013, 05:14:28 PM

    > (round 5 1)
    10
    > (round 0.5)
    0
    > (round 0.05 -1)
    0.1

    Seems to be a bug: at least not consistent.



    My version: newLISP v.10.5.4 64-bit on Linux IPv4/6 UTF-8 libffi.
    #33
    .. with a simple patch for a first solution and further ideas for possibly better ones...

    [4. update] Removed some stupid patch ideas.

    [3. update]

    Solution for 10.5.4: http://www.newlispfanclub.alh.net/forum/viewtopic.php?f=16&t=4416#p21792">http://www.newlispfanclub.alh.net/forum/viewtopic.php?f=16&t=4416#p21792

    Solution for later versions: http://www.newlispfanclub.alh.net/forum/viewtopic.php?f=16&t=4416#p21794">http://www.newlispfanclub.alh.net/forum/viewtopic.php?f=16&t=4416#p21794

    '[HowTo]' added to title.

    [2. update] Changed title away from feature request: some discussion revealed a very good solution (see posts thereafter); and the patches are not a good idea compared with it.

    [update] Better patch - IMHO - in self-reply to this post.



    Doing more and more scripting with newLISP, it's important to me to have interactive debugging facilities.

    Furthermore it's good to have some separation between CLI args targeted to the interpreter and others targeted to a script interpreted by it.



    Assumed there is the following code:

    #!/usr/bin/newlisp

    (module "getopts.lsp")

    (shortopt "p" (println "players: " getopts:arg) "int" "num of players")
    (shortopt "h" (println (getopts:usage)) nil "usage")

    (getopts (2 (main-args)))

    ;; some code here: if something goes wrong, or just for interactive development
    ;; no (exit) could be a good idea here...
    (exit)

    This gives:

    sr@free:~/NewLisp$ ./game.lsp -p 10
    players: 10
    sr@free:~/NewLisp$

    So far so good.



    But if something goes wrong, or we just want to inspect some symbols before (exit), commenting out (exit) does not work:

    #!/usr/bin/newlisp

    (module "getopts.lsp")

    (shortopt "p" (println "players: " getopts:arg) "int" "num of players")
    (shortopt "h" (println (getopts:usage)) nil "usage")

    (getopts (2 (main-args)))

    ;; some code here: if something goes wrong, or just for interactive development
    ;; no (exit) could be a good idea here...
    ;(exit)

    We get:

    sr@free:~/NewLisp$ ./game.lsp -p 10
    players: 10
    newLISP server setup on 10 failed.
    sr@free:~/NewLisp$

    instead of entering newLISP interpreter.



    [4. update]: Removed some stupid patch ideas.



    What do you think?
    #34
    [Update: -> feature]

    I'm wondering why switching contexts inside a function does not work in the example below.

    Especially because switching inside a func from a non-MAIN context to MAIN already has worked.



    The code:

    ; fails
    (define (create-funcs)
      (println "in create-funcs")
      (context 'C)
      (define (f_1) 1)
      (context MAIN))
    (create-funcs)
    (symbols 'C)  "-> does not work: no sym defined"
    ;;
    ;; works
    (context 'C)
    (define (f_1) 1)
    (context MAIN)
    (symbols 'C) "-> OK: sym defined"

    The session:

    sr@free:~/NewLisp$ newlisp
    newLISP v.10.5.4 64-bit on Linux IPv4/6 UTF-8 libffi, options: newlisp -h

    >
    ;; fails
    (define (create-funcs)
      (println "in create-funcs")
      (context 'C)
      (define (f_1) 1)
      (context MAIN))
    (create-funcs)
    (symbols 'C)  "-> does not work: no sym defined"
    ;;
    ;; works
    (context 'C)
    (define (f_1) 1)
    (context MAIN)
    (symbols 'C) "-> OK: sym defined"

    (lambda () (println "in create-funcs") (context 'C)
     (define (f_1)
      1)
     (context MAIN))
    in create-funcs
    MAIN
    ()
    "-> does not work: no sym defined"
    C
    (lambda () 1)
    MAIN
    (C:f_1)
    "-> OK: sym defined"
    >

    It would be nice to be able to introduce (and later delete) contexts this way (testing is my current use-case triggering this problem).
    #35
    Influenced by a C++ background I've tried to get some assert()-like functionality. Here are the v2.1 results:

    (context 'assert)
    ;;
    (define (assert-expr expr caller-sym no-in-func)
      (++ call_count)
      (if (not (eval expr))
          (throw-error
           (append
            "assert failed for expr: " (string expr)
            (if caller-sym ; implies no-in-func
                (let (caller-fun (eval caller-sym))
                  (if (not (or (lambda? caller-fun) (macro? caller-fun)))
                      (append
                       " (caller " (string caller-sym) " is not a lambda/macro)")
                      (append
                       "n-> " (string no-in-func) ". assert called from "
                       (string caller-sym))))
                ""))))
      true)
    ;;
    (define-macro (assert:assert expr caller-sym no-in-func)
      (assert-expr expr caller-sym no-in-func))
    (set 'pre assert:assert)
    ;;
    (set 'assert_ctx_sym (sym 'assert MAIN))
    (define (assertSyms-cmp ignored e)
      (or
       (= e assert_ctx_sym) ; 'assert would lead to 'assert:assert
       (= e 'assert:assert)
       (= e 'assert:pre)))
    ;;
    ;; inplace mod of a ref-all'ed list referenced by rsym
    (define (sym-mod-ref-all rsym func-sym-mod-ref exp-key func-compare)
      (assert (list? (eval rsym)))
      (local (modFlag)
        (dolist
         (r (if func-compare ;; nil arg is different from no arg here ..
                (ref-all exp-key (eval rsym) func-compare)
                (ref-all exp-key (eval rsym)))) ; .. no nil arg!
         (if (func-sym-mod-ref rsym r exp-key)
             (setq modFlag true)))
        modFlag))
    ;;
    (define (assertCall-sym-mod-ref caller-sym assert-ref exp-key-ignored)
      (local (modFlag)
        (if (= (last assert-ref) 0) ; func call pos?
            (letn ((aref (0 -1 assert-ref)) ; one level up of assert sym
                   (alist (nth aref (eval caller-sym)))) ; nth avoids func call
              (if (<= (length alist) 2) ; (assert) or (assert expr)
                  (begin
                    (if (= (length alist) 1)          ; check for naked assert
                        (push nil alist -1))          ; -> add missing nil expr.
                    (push caller-sym alist -1)        ; add more ..
                    (push (++ no-in-func) alist -1)   ; .. info.
                    (setq modFlag true)
                    ;; replace (args) calls in assert by local-args ..
                    (let (argsRefs (ref-all 'args alist) argsFlag nil)
                      (if (not (null? argsRefs))
                          (begin
                            (dolist
                             (e argsRefs)
                             (if (length (alist (-1 e)) 1) ; just (args)
                                 (begin
                                   (setq argsFlag true)
                                   (setq (alist (0 -1 e)) 'assert:local-args))))
                            ;; .. inited before calling assert.
                            (if argsFlag
                                (setq alist
                                      (list 'begin
                                            '(setq assert:local-args (args))
                                            alist))))))
                    (setq (nth aref (eval caller-sym)) alist)))))
        modFlag))
    ;;
    ;; add missing args of assert calls
    (define (tweak-arguments rsym)
      (if (or (lambda? (eval rsym)) (macro? (eval rsym)))
          (local (no-in-func)
            (if (sym-mod-ref-all rsym assertCall-sym-mod-ref nil assertSyms-cmp)
                (println (string no-in-func) " (assert) call"
                         (if (> no-in-func 1) "s" "") " modified in: " rsym)))))
    ;;
    ;; asserts in contexts
    ;;
    (define (tweak-context ctx)
      (dolist (s (symbols ctx))
              (tweak-arguments s)))
    (define (tweak-contexts contexts)
      (dolist (ctx contexts)
              (tweak-context ctx)))

    (context MAIN)


    Why this is so long is best explained by - a simplified - use-case:

    ;;; [insert assert code from above]
    ;;
    ;; { copy'n'paste
    ;;
    ;; simplified use-case for assert demo purposes
    ;;
    (context 'db_proto)
    ;;
    (define (init dataCtx metaCtx transCtx)
      (setq data dataCtx)
      (setq meta metaCtx)
      (setq trans transCtx)
      (context))
    ;;
    (define (get-data-1 id) ; assert list arg
      (assert:pre (and
                   (string? id)
                   (not (nil? (data id)))))
      (data id))
    ;;
    (define (get-data-2 id) ; assert non-list arg, assert call enumeration
      (assert:pre (string? id))
      (assert (not (nil? (data id))))
      (data id))
    ;;
    (context MAIN)
    ;;
    (context 'db)
    ;;
    (define (create)
      (let (tmp (args))
        (assert (null? tmp)))
      (++ dbCount)
      (context MAIN)
      (let ((db_sym (sym (append "DB_" (string dbCount))))
            (data_sym (sym (append "Data_" (string dbCount)))))
        (let (db_ctx (new db_proto db_sym)) ; transfer syms from db_proto: to DB_*
          (db_ctx:init ; call :init
           (new Tree data_sym)))))
    (define (create_alt)
      (assert (null? (args)))
      (++ dbCount)
      (context MAIN)
      (let ((db_sym (sym (append "DB_" (string dbCount))))
            (data_sym (sym (append "Data_" (string dbCount)))))
        (let (db_ctx (new db_proto db_sym)) ; transfer syms from db_proto: to DB_*
          (db_ctx:init ; call :init
           (new Tree data_sym)))))
    ;;
    (context MAIN)
    ;;
    ;; } copy'n'paste

    ;; { copy'n'paste
    ;;
    ;; 1. test without assert call modifications
    ;; init
    (set 'dbc (db:create))
    (dbc:data "foo" "bar") ; put something into data
    ;;
    ;; } copy'n'paste
    ;;

    ;; try manually from here
    ;; a.
    (db:create "forbidden arg")
    ;; -> this gives db:create info *without* tweaking asserts
    (db:create_alt "forbidden arg")
    ;; -> this does not work without tweaking (should throw)
    ;; b.
    (dbc:get-data-1) ; missing id
    (dbc:get-data-2) ; missing id
    ;; c.
    (dbc:get-data-1 "wrong id")
    (dbc:get-data-2 "wrong id")
    ;; d.
    (dbc:get-data-1 "foo") ; correct id
    (dbc:get-data-2 "foo") ; correct id
    ;;

    ;; { copy'n'paste
    ;;
    ;; 2. test with assert call modifications
    ;; modify assert calls
    (assert:tweak-contexts '(db db_proto))
    ;; short form for
    ;;   (assert:tweak-context db) (assert:tweak-context db_proto)
    ;;
    ;; (assert:tweak-context db_proto)
    ;;   short from for:
    ;;     (assert:tweak-arguments 'db_proto:get-data-1)
    ;;     (assert:tweak-arguments 'db_proto:get-data-2)
    ;;
    ;; just one func:
    ;;   (assert:tweak-arguments 'db:create)
    ;;
    ;;
    ;; init of new db (old one contains unmodified calls)
    (set 'dbc (db:create))
    (dbc:data "foo" "bar") ; put something into data
    ;;
    ;; } copy'n'paste
    ;;
    ;; try manually from here
    ;; a.
    (db:create "forbidden arg")
    ;; -> db:create info redundant after modifying asserts
    (db:create_alt "forbidden arg")
    ;; -> this does work now after tweaking (throws)
    ;; b.
    (dbc:get-data-1) ; missing id
    (dbc:get-data-2) ; missing id
    ;; c.
    (dbc:get-data-1 "wrong id")
    (dbc:get-data-2 "wrong id")
    ;; d.
    (dbc:get-data-1 "foo") ; correct id
    (dbc:get-data-2 "foo") ; correct id
    ;;


    Case a. (db:create "forbidden arg") is 'interesting', which creates redundant caller info after tweaking asserts by (assert:tweak-contexts '(db db_proto)).

    In the other cases added caller info would be missing without tweaking asserts: it won't be shown as part of output of throw callstacks (this has been a reason to start with these tweaks).



    To avoid calling (args) in a wrong function context, a temp var will be inserted in this case; see (db:create) after tweaking its assert.



    Any suggestions for improvements are welcome (I like to learn).



    Possible extensions are tweaks for:

    [*] switching assert checks on/off (selectively: assert:pre macros could be on, others off)
  • [*] code coverage statistics
  • [/list]


    Versions:

       
    [*]v2.1.1: [fix] use-case code
     
  • [*]v2.1: [enh] context-wide tweak of asserts

  •  
  • [*]v2:
     

         
    [*][fix] simplified interface: (and)'ed expressions easily replace - unneeded - quoted list args (without lack of functionality)

  •    
  • [*][enh] (args) call in assert expr gives caller args now (by introducing var for storing its local value)

  •   [/list]

     
  • [*]v1: first shot
  • [/list]
    #36
    A 'ref agnostic' copy called ra_copy leaves args of functions, which themselves are using destructive functions applied to them, unchanged; even if these args are contexts referencing default functors (containing the val of the arg).

    Here is the code of ra_copy and a predicate functor? used by it:

    [Update: improved version in later post.]

    ;; functor? predicate
    (define (functor? arg)
      (and
       (context? arg)
       ;; (not (nil? (... rets bool instead of functor copy -> faster
       (not (nil? (context arg (sym (string arg)))))))
    ;;
    ;; 'ref agnostic' copy
    (define (ra_copy arg (defaultIfNil nil)) ; defaultIfNil arg may be "" or '()
      (if (context? arg)
          (if (functor? arg)
              (context arg (sym (string arg)))
              defaultIfNil)
          (if (nil? arg)
              defaultIfNil
              arg)))

    Why this?

    Assumed you want to replace '$' chars in a string in two variants by '#' and by ";;"; leaving the original string intact.

    Let code speak for itself: there are three versions of replace-dollar-by-hash-n-semicolons without copy, with copy and with ra_copy in the following example; both results will be returned together in a list.

    Just copy/paste all into the interpreter window for getting results:

    ;; functor? predicate
    (define (functor? arg)
      (and
       (context? arg)
       ;; (not (nil? (... rets bool instead of functor copy -> faster
       (not (nil? (context arg (sym (string arg)))))))
    ;;
    ;; 'ref agnostic' copy
    (define (ra_copy arg (defaultIfNil nil)) ; defaultIfNil arg may be "" or '()
      (if (context? arg)
          (if (functor? arg)
              (context arg (sym (string arg)))
              defaultIfNil)
          (if (nil? arg)
              defaultIfNil
              arg)))
    ;;
    ;;
    "----------"
    (set 's "$ firstn $ secondn $ thirdn")
    (set 'S:S s)
    "no copy..."
    (define (replace-dollar-by-hash-n-semicolons str)
      (list
       (replace "$" str ";;")
       (replace "$" str "#")))
    ;;
    (replace-dollar-by-hash-n-semicolons s) "  -> KO"
    (replace-dollar-by-hash-n-semicolons S) "  -> KO"
    "OK s:" s
    "KO S:S :" S:S
    ;;
    ;;
    "----------"
    (set 's "$ firstn $ secondn $ thirdn")
    (set 'S:S s)
    "copy..."
    (define (replace-dollar-by-hash-n-semicolons str)
      (list
       (replace "$" (copy str) ";;")
       (replace "$" (copy str) "#")))
    ;;
    (replace-dollar-by-hash-n-semicolons s) "  -> OK"
    (replace-dollar-by-hash-n-semicolons S) "  -> KO"
    "OK s:" s
    "KO S:S :" S:S
    ;;
    ;;
    "----------"
    (set 's "$ firstn $ secondn $ thirdn")
    (set 'S:S s)
    "ra_copy..."
    (define (replace-dollar-by-hash-n-semicolons str)
      (list
       (replace "$" (ra_copy str) ";;")
       (replace "$" (ra_copy str) "#")))
    ;;
    (replace-dollar-by-hash-n-semicolons s) "  -> OK"
    (replace-dollar-by-hash-n-semicolons S) "  -> OK"
    "OK s:" s
    "OK S:S :" S:S
    ;;

    Results:

    [*]without copy does not work at all (because of using destructive function replace twice);
  • [*]with copy works for vals, but not for refs (both result and original are wrong);

  • [*]ra_copy works for both.
  • [/list]


    This approach can be extended by a function (builtin or macro (?)) like

    [*] copy-if-functor: for conditionally copying just in case some symbol evaluates to a context with default functor (and avoiding copying in other cases).[/list]

    Note:

    In the example above copy-if-functor wouldn't be sufficient, due to having destructive function replace twice (but it may be an optimization for other cases).



    Postscript:

    Due to being a newbie regarding newLISP, there may be errors in terminology and problems with this approach I don't see so far.
    #37
    Trying to understand the semantics of copy I've found differences between copying - ordinary - unreferenced lists and context referenced ones: coypy does not copy context referenced lists.

    This can change the sematics of code, if sometime later unreferenced lists will be replaced by referenced ones.



    The manual says:
    Quote
    copy

    syntax: (copy exp)



    Make a copy from evaluating expression in exp. Some built-in functions are destructive, changing the original contents of a list, array or string they are working on. With copy their behavior can be made non-destructive.

    Trying to check this non-destructive behavior I've tried with unreferenced and context referenced lists.



    Following code:

    "(setf c '()): " (setf c '())
    (push "s_0" c) (push "s_1" c) (push "s_2" c)
    "set-ref no copy......."
    (set-ref (c 1) c 1) "c: " c
    "set-ref with copy......."
    (set-ref (c 1) (copy c) 2) "c: " c
    ""
    "(define C:C): " (define C:C)
    (push "s_0" C) (push "s_1" C) (push "s_2" C)
    "set-ref no copy......."
    (set-ref (C 1) C 1) "C:C : " C:C
    "#### set-ref with copy......."
    (set-ref (C 1) (copy C) 2) "C:C : " C:C
    ""
    "trying to improve" ;; Update: code contains superfluous copy ops.
    (define (alt_copy var)
      (if (context? var)
          (let (v (context var (sym (string var))))
            (if (nil? v)
                '()
                (copy v)))
          (copy var)))
    ""
    "(setf c '()): " (setf c '())
    (push "s_0" c) (push "s_1" c) (push "s_2" c)
    "set-ref no copy......."
    (set-ref (c 1) c 1) "c: " c
    "set-ref with copy......."
    (set-ref (c 1) (alt_copy c) 2) "c: " c
    ;;
    "(define C:C): " (define C:C)
    (push "s_0" C) (push "s_1" C) (push "s_2" C)
    "set-ref no copy......."
    (set-ref (C 1) C 1) "C:C : " C:C
    "#### set-ref with copy......."
    (set-ref (C 1) (alt_copy C) 2) "C:C : " C:C
    ;;

    gives following differences between copy and alt_copy:

    ;; copy
    "#### set-ref with copy......."
    ("s_2" 2 "s_0")
    "C:C : "
    ("s_2" 2 "s_0")

    ;; alt-copy
    "#### set-ref with alt_copy......."
    ("s_2" 2 "s_0")
    "C:C : "
    ("s_2" 1 "s_0")


    The advantage of alt_copy is, that it behaves the same, if an unreferenced list will later be replaced by a refernced one (e.g. for performance reasons).



    Are there reasons not to have this as the default behavior?



    Update after Lutz' reply

    alt_copy contains superfluous copy operations and has other deficiencies: do not use it as it is! See later post for a better version.
    #38
    Hello All,

    hello Lutz,



    further stress testing newLisp: is this a bug or a feature (possibly for efficiency reasons)?



    This leads to an endless loop:

    newLISP v.10.5.3 64-bit on Linux IPv4/6 UTF-8, options: newlisp -h

    >
    (context 'Ctx)
    (context MAIN)
    (context? Ctx)
    (context 'Ctx2)
    (context MAIN)
    ;;
    (set 'Ctx:sub Ctx2)
    ;;
    (push '("foo" "bar") Ctx:sub)
    ;;
    ;; not OK:
    (delete 'Ctx2)
    (assoc "foo" Ctx:sub) ; -> endless loop
    ;; copy/paste until here ;;

    Ctx
    MAIN
    true
    Ctx2
    MAIN
    Ctx2
    (("foo" "bar"))
    true
    ^CERR: received SIGINT - in function assoc
    (c)ontinue, (d)ebug, e(x)it, (r)eset:x


    But doubling the delete - after the first delete Ctx2 remains visible in the output of '(symbols)' - repairs the problem (gives a correct error message):

    newLISP v.10.5.3 64-bit on Linux IPv4/6 UTF-8, options: newlisp -h

    >
    (context 'Ctx)
    (context MAIN)
    (context? Ctx)
    (context 'Ctx2)
    (context MAIN)
    ;;
    (set 'Ctx:sub Ctx2)
    ;;
    (push '("foo" "bar") Ctx:sub)
    ;;
    ;; OK:
    (delete 'Ctx2)
    (delete 'Ctx2)
    (assoc "foo" Ctx:sub) ; -> expected error message
    ;; copy/paste until here ;;

    Ctx
    MAIN
    true
    Ctx2
    MAIN
    Ctx2
    (("foo" "bar"))
    true
    true

    ERR: list expected in function assoc : nil
    >


    Due to being related to symbol lookup there could be some relation to 'pop-assoc problem: feels like a bug' (http://www.newlispfanclub.alh.net/forum/viewtopic.php?f=16&t=4377#p21584">http://www.newlispfanclub.alh.net/forum/viewtopic.php?f=16&t=4377#p21584).





    Best regards,

    Stephan
    #39
    Hello All,

    hello Lutz,



    The following shows something, which looks like a bug.



    Code:

    (new Tree 'Data)
    (push '(1 (k_1 "v_1")) Data)
    (push '(k_2 "v_2") (assoc 1 Data) -1)
    ;; -> OK
    ;;
    (pop-assoc '(1 k_2) Data)
    Data:Data
    ;; -> OK
    ;;
    ;;
    (push '(k_2 "v_2") (assoc 1 Data) -1)
    ;; -> fails!
    ;;
    ;; repair
    (push (pop (assoc 1 Data) -1) (assoc 1 Data) -1)
    ;;
    (push '(k_2 "v_2") (assoc 1 Data) -1)
    ;; -> works again.

    Session:

    newLISP v.10.5.3 64-bit on Linux IPv4/6 UTF-8, options: newlisp -h

    > (new Tree 'Data)
    Data
    > (push '(1 (k_1 "v_1")) Data)
    ((1 (k_1 "v_1")))
    > (push '(k_2 "v_2") (assoc 1 Data) -1)
    (1 (k_1 "v_1") (k_2 "v_2"))
    > ;; -> OK
    > ;;
    > (pop-assoc '(1 k_2) Data)
    (k_2 "v_2")
    > Data:Data
    ((1 (k_1 "v_1")))
    > ;; -> OK
    > ;;
    > ;;
    > (push '(k_2 "v_2") (assoc 1 Data) -1)
    (1 (k_1 "v_1"))
    > ;; -> fails!
    > ;;
    > ;; repair
    > (push (pop (assoc 1 Data) -1) (assoc 1 Data) -1)
    (1 (k_1 "v_1"))
    > ;;
    > (push '(k_2 "v_2") (assoc 1 Data) -1)
    (1 (k_1 "v_1") (k_2 "v_2"))
    > ;; -> works again.

    It would be nice, if someone could check this at his/her own host, for checking platform differences (I had to compile with a configure-alt step in advance, to get all tests running).



    Best regards,

    Stephan