HackerTrans
TopNewTrendsCommentsPastAskShowJobs

homedirectory

no profile record

comments

homedirectory
·2 ปีที่แล้ว·discuss
From the article:

> I want it to be the case that this function only actually calls do-something-very-expensive once per unique value of x, even across separate invocations of dumb-example.

My code memoizes results of function FIB _inside_ the lambda assigned to EXAMPLE, even across separate invocations of EXAMPLE.
homedirectory
·2 ปีที่แล้ว·discuss
You can achieve memoization of an expression inside a function without any global state and macros:

    (defun compute-hash (key hash f)
      "Get the value for KEY in HASH or compute it with F, enter into HASH and return."
      (multiple-value-bind (val win)
          (gethash key hash)
        (if win
            val
            (setf (gethash key hash) (funcall f key)))))

    (defun memoized (f)
      (let ((cache (make-hash-table)))
        (flet ((memo (x g) (compute-hash x cache g)))
          (lambda (&rest args) (apply f #'memo args)))))

    (defun fib (n)
      (if (<= n 1)
          n
          (+ (fib (1- n)) (fib (- n 2)))))

    ;; MEMO is a function that takes a key and a computing function.
    ;; If a key has been memoized, it returns the cached value, otherwise it calls the computing
    ;; function with the key and caches the result.
    (let ((example (memoized (lambda (memo x)
                               (format t "X: ~a~%" x)
                               (let ((result (funcall memo x #'fib)))
                                 (format t "~a~%" (* 2 result)))))))
      (trace fib)
      (funcall example 5)
      (funcall example 5)
      (funcall example 5)
      (untrace fib))
homedirectory
·2 ปีที่แล้ว·discuss
Your fear of macros is natural, since they are not typical for a programming language. It is expected that a newcomer to Lisp is likely to have a hard time grasping macros.

You should not be disregarding one of the most powerful features of Lisp due to a lack of understanding. Imagine if a Haskell programmer rejected typeclasses because they are initially difficult to write and potentially difficult to grasp.

If you want a taste of the power of Lisp macros, check out "Monad Macros in Common Lisp" by David Sorokin (https://cl-monad-macros.common-lisp.dev/monad-macros.htm).
homedirectory
·2 ปีที่แล้ว·discuss
I also once thought that the magic underlying SETF has access to true L-values, and had also been surprised by it being macros under the hood.

Here are my thoughts on the article:

Firstly, there are errors in the definition of OUR-SETF macro:

1. (symbol-function ,our-setf-function-name) will signal an unbound variable error. ,our-setf-function-name must be quoted: (symbol-function ',our-setf-function-name)

2. Arguments to APPLY are ill-formed. Instead of CONS, LIST must be used.

    (apply (symbol-function ',our-setf-function-name)
           (cons ,new-value ,@(cdr locator)))
Using CONS, (our-setf (head (list 1 2)) 0) expands to:

    (apply (symbol-function '|(our-setf head)|) 
           (cons 0 (list 1 2)))
Which is equivalent to (|(our-setf head)| 0 1 2), clearly not what we want.

Furthermore, an OUR-SETF that accepts multiple places will fail. Consider an example from the article:

    (our-setf (aref a 23) 0)
It expands to:

    (|(our-setf aref)| (cons 0 a 23))
Clearly, an error.

The correct usage of APPLY is:

    (apply (symbol-function ',our-setf-function-name)
           (list ,new-value ,@(cdr locator)))
Alternatively, use a FUNCALL:

    (funcall (symbol-function ',our-setf-function-name)
             ,new-value ,@(cdr locator))
Also, SYMBOL-FUNCTION can be dropped, as both FUNCALL and APPLY accept a function designator.

    (funcall ',our-setf-function-name ,new-value ,@(cdr locator))
This concludes the errors part.

Secondly, I was really confused by the symbol generation for OUR-SETF example. I thought of function-defining macros, such as DEFUN and DEFMETHOD, and they accept lists of the form (SETF X) and not symbols whose name looks like a list. This latter notation could be better explained by using multiple escape characters from the Common Lisp HyperSpec. For example, the bar character: |(our-setf head)|.

Multiple escape characters also mean that symbol generation is needed only in OUR-SETF macro. Generic functions and methods can be defined directly:

    (defgeneric |(OUR-SETF HEAD)| (new-value place))

    (defmethod |(OUR-SETF HEAD)| (new-value (place list)) 
      (rplaca place new-value)
      new-value)
This would also require changes to OUR-SETF macro because the symbols used to name generic functions and methods are now interned in a package.

    (defmacro our-setf (locator new-value)
      (let* ((selector (car locator))
             ; use the selector's package,
             ; selector must be interned
             (our-setf-function-name (intern (format nil "(OUR-SETF ~a)"
                                              selector) 
                                     (symbol-package selector))))
        `(funcall ',our-setf-function-name
                  ,new-value ,@(cdr locator))))
With this change we can even use selectors from other packages.

As the author said, these are symbols with weird names. So we can remove most of the weirdness with more macros:

    (eval-when (:compile-toplevel :load-toplevel :execute)
      (defun selector-symbol (selector)
        (or (get selector 'our-setf-name)
            (gentemp (string selector) (symbol-package selector)))))

    (defmacro defgeneric-setf ((selector &rest selector-params) (new-value))
      (let ((name (selector-symbol selector)))
        `(progn
           (setf (get ',selector 'our-setf-name) ',name)
           (defgeneric ,name (,new-value ,@selector-params)))))

    (defmacro defmethod-setf ((selector &rest selector-params) (new-value) &body body)
      (let ((name (selector-symbol selector)))
        `(progn
           (setf (get ',selector 'our-setf-name) ',name)
           (defmethod ,name (,new-value ,@selector-params)
             ,@body))))

    (defmacro our-setf ((selector &rest selector-params) new-value)
      (let ((our-setf-function-name (selector-symbol selector)))
        `(funcall ',our-setf-function-name ,new-value ,@selector-params)))

    (defgeneric-setf (head x) (new-value))

    (defmethod-setf (head (x cons)) (new-value)
      (rplaca x new-value)
      new-value)

    #+nil
    (let ((xs (list 1 2))) 
      ; expands to (funcall 'headN 0 xs) where N is a number from GENTEMP
      (our-setf (head xs) 0) 
      xs) ; => (0 2)

    (defgeneric-setf (our-elt seq idx) (new-value))

    (defmethod-setf (our-elt (seq list) idx) (new-value)
      (loop for i from 0 to idx
            for cons on seq
            finally (our-setf (head cons) new-value))
      new-value)

    #+nil
    (let ((xs (list 'a 'b 'c))) 
      ; expands to (funcall 'our-eltN 'k xs 1) where N is a number from GENTEMP
      (our-setf (our-elt xs 1) 'k) 
      xs) ; => (a k c)
homedirectory
·2 ปีที่แล้ว·discuss
Thank you for sharing this demo. Amazing stuff! Inspired me to start working on improvements to the development environment of my team.
homedirectory
·3 ปีที่แล้ว·discuss
Thanks. This feature is described as optional for 3rd party apps and a claim is made that the information is not sent off the device. Assuming this is true, I take back my criticism about the policy being invasive.
homedirectory
·3 ปีที่แล้ว·discuss
See "Privacy and Security" section.

> And it [Communication Safety] extends beyond Messages to include content sent and received in AirDrop, ..., and third‑party apps.

This distorted Apple's vision of privacy is not news to me, but extending their invasive policy to third-party apps is crossing the line. However, I'm not sure whether this means that they are going to forcefully intercept all content from 3rd party apps, or introduce an API to app developers that will allow them to flag content as sensitive. If anybody knows more about this, please let us all know.