ra_copy: 'ref agnostic' copy

Started by hartrock, August 16, 2013, 09:58:14 PM

Previous topic - Next topic

hartrock

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.

    Lutz

    #1
    Instead of:

    (not (nil? (context arg (sym (string arg)))))

    you can do shorter:

    (true? (context arg (string arg)))

    But note: both ways will return also nil if the default functor exists but contains nil and also () in the second case. That might not be what you want depending on the application.



    The following version will test only for the existence of the default functor and the result would be independent of the contents of an existing default functor:



    (true? (sym (string arg) arg nil))

    hartrock

    #2
    First thanks for pointing out some subtle things.
    Quote from: "Lutz"Instead of:

    (not (nil? (context arg (sym (string arg)))))

    you can do shorter:

    (true? (context arg (string arg)))

    But note: both ways will return also nil if the default functor exists but contains nil and also () in the second case. That might not be what you want depending on the application.

    I want to have the semantics of the former, but the latter contains an improvement useable for the former as:

    (not (nil? (context arg (string arg))))

    Quote from: "Lutz"
    The following version will test only for the existence of the default functor and the result would be independent of the contents of an existing default functor:

    (true? (sym (string arg) arg nil))

    OK: I see now, that the naming of functor? has been bad, because the question is, if the value of a functor is not nil, if it is existing at all.

    BTW: Naming of things is very important, because you are thinking in these terms; and fuzzy naming makes sharp thinking more difficult...



    An improved version of ra_copy with some comments:

    [Update 2013-08-26: further improvements in next post of mine.]

    ;; 'Ref agnostic' copy
    ;; (ra_copy arg (defaultIfNil nil))
    ;; Checks first, if there is a (context) ref arg or a (not a context) val arg.
    ;; Then it returns
    ;; - copy of val arg, if arg is not nil;
    ;; - copy of dereferenced ref arg,
    ;;   if arg is referencing a value not nil (but see limitation below);
    ;; - defaultIfNil, otherwise (its default is nil).
    (define (ra_copy arg (defaultIfNil nil)) ; defaultIfNil arg may be "" or '()
      (if (context? arg)
          ;; ref arg
          (if (not (nil? (context arg (string arg)))) ; ref to non-nil val?
              (context arg (string arg)) ; return dereferenced val
              defaultIfNil)
          ; val arg
          (if (nil? arg)
              defaultIfNil
              arg)))
    ;; Notes:
    ;; - copy being made by just returning from function;
    ;; - missing default functor treated as ref to nil val;
    ;; - limitation: if referenced value is itself a context, referenced context
    ;;   will be returned (due to copy'ing a context returns itself);
    ;; - temp var for '(context arg (string arg))' not introduced to avoid an
    ;;   additional copy.

    The limitation says, that there is no copying of non-refs, which are indirectly referenced via multiple dereference steps: this is refs of refs of non-refs (or more indirection). This could be 'repaired' by calling ra_copy recursively, but this wouldn't fit to the semantics of context references, which is to stop dereferencing at the value of the default functor (e.g. push to a context referencing another context does not work).



    Part of the code could be factored out into:

    [Update 2013-08-26: see next post of mine.]

    ;; (referee-not-nil? ctx)
    ;; Precondition: ctx is context.
    ;; Returns
    ;; - true, if
    ;;   - ctx has a default functor, and
    ;;   - ctx's default functor's value (referee) is not nil;
    ;; - nil otherwise.
    ;; Note: a non-existing default functor will be treated as reference to nil (referee).
    (define (referee-not-nil? ctx)
       (not (nil? (context ctx (string ctx)))))
    ;; Note: (not (nil? (... rets bool instead of referee (copy) -> faster


    I'm open for improvements regarding terminology: don't want to confuse, if a used term already has another meaning common for newLISP. Difficult is the wording regarding references: there is a context reference, which has a default functor (or not), which has a value, which is context reference's referee, which itself may be a context reference...

    rickyboy

    #3
    Quote from: "hartrock"BTW: Naming of things is very important, because you are thinking in these terms; and fuzzy naming makes sharp thinking more difficult...

    Hear, hear!
    (λx. x x) (λx. x x)

    hartrock

    #4
    After detecting (default context) (http://www.newlisp.org/downloads/newlisp_manual.html#default">http://www.newlisp.org/downloads/newlis ... ml#default">http://www.newlisp.org/downloads/newlisp_manual.html#default) there are some simplifications leading - surprise, surprise - to shorter run times:

    ;; 'Ref agnostic' copy
    ;; (ra_copy arg (defaultIfNil nil))
    ;; Checks first, if there is a (context) ref arg or a (not a context) val arg.
    ;; Then it returns
    ;; - copy of val arg, if arg is not nil;
    ;; - copy of dereferenced ref arg,
    ;;   if arg is referencing a value not nil (but see limitation below);
    ;; - defaultIfNil, otherwise (its default is nil).
    (define (ra_copy arg (defaultIfNil nil)) ; defaultIfNil arg may be "" or '()
      (if (context? arg)
          ;; ref arg
          (if (not (nil? (default arg))) ; ref to non-nil val?
              (default arg) ; return dereferenced val
              defaultIfNil)
          ; val arg
          (if (nil? arg)
              defaultIfNil
              arg)))
    ;; Notes:
    ;; - copy being made by just returning from function;
    ;; - missing default functor treated as ref to nil val;
    ;; - limitation: if referenced value is itself a context, referenced context
    ;;   will be returned (due to copy'ing a context returns itself).

    Slowly writing more and more code in newLISP my impression now is - coming from a Smalltalk (all vars are pointers, and you have to think about the depth of copying referenced objects) and a C++ background (pointers are dangerous, if you are sloppy with them) -, that there are less use-cases for this function than I've thought at first. The risk of breaking functionality by switching from vals to refs by the user of some lib func stays, though. Don't know, if it's a good idea to develop this 'ref agnostic' idea further by following indirect references (contexts) further until at last a non-reference (non-context) will be found. Any experiences taken from practice?



    And (referee-not-nil? ctx) boils down to:

    ;; (referee-not-nil? ctx)
    ;; Precondition: ctx is context.
    ;; Returns
    ;; - true, if
    ;;   - ctx has a default functor, and
    ;;   - ctx's default functor's value (referee) is not nil;
    ;; - nil otherwise.
    ;; Note: a non-existing default functor will be treated as reference to nil
    ;;       (referee).
    (define (referee-not-nil? ctx)
       (not (nil? (default ctx))))
    ;; Note: (not (nil? (... rets bool instead of referee (copy) -> faster

    This is hardly worth the effort and - even worse - hides commonly known - even by me now ;-) - functionality by wrapping it into a func with a new name to be learned...