I am using the Racket Objective-C FFI to embed a WebKit WebView in an app.
In order to receive page-load notifications I am creating a new ObjC class in Racket that is set as the frame-load delegate of the web-view.
The class looks something like:
(define-objc-class MyWebFrameLoadDelegate NSObject
[]
(- _void (webView: [_id wv] didFinishLoadForFrame: [_id wf])
(send frame set-status-text "Page Loaded")))
When running the code within DrRacket it works great the first time. Further iterations cause the process to die:
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x0000000000000020
...
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 libobjc.A.dylib 0x00007fff8511e299 objc_registerClassPair + 63
1 Racket 0x00000001002d329c ffi_call_unix64 + 76
2 Racket 0x00000001002d3eb4 ffi_call + 644
3 Racket 0x00000001002c612f ffi_do_call + 1599
4 ??? 0x00000001004b50cc 0 + 4299903180
...
... which seems to indicate that it is the re-definition of the ObjC class that is the cause.
Is there a clean way using the FFI, or more general Racket features, to avoid re-defining the class if it already exists ?
I tried wrapping define-objc-class in a conditional but it needs to be a top-level form.
I could drop down to the raw ObjC runtime functions and define the delegate class on the fly - but it would be nice to avoid that.
Solution - wrap the define-objc-class in a let to define the class in a nested scope inside a conditional:
(define MyWebFrameLoadDelegate
(or (objc_lookUpClass "MyWebFrameLoadDelegate")
(let ()
(define-objc-class MyWebFrameLoadDelegate NSObject
[]
(- _void (webView: [_id wv] didFinishLoadForFrame: [_id wf])
(send frame set-status-text "Page Loaded")))
MyWebFrameLoadDelegate)))
I don't think define-obj-class must be used at top level. It is a definition form, though, so you can't use it as an expression. You should be able to do something like the following:
(define MyClass
(or ___ ;; find the class, if it already exists
(let ()
(define-objc-class MyClass ___)
MyClass)))
BTW, it looks like a fix for the crashing behavior was committed on March 7.
Just in case you missed it:
https://github.com/shekari/racket-webkit/blob/master/webkit.rkt
Enjoy,
Related
Emacs version: 26.3
Slime version: 2.26.1
I start up Emacs.
I open up a simple .lisp file.
(defun testfn (x y)
(+ x y))
(defmacro testmc (form)
form
`(list 1 2 3))
I place my cursor over the symbol defun and issue the keyboard-command M-. (slime-edit-definition).
This should bring me to the definition of defun.
But it doesn't.
It brings me here:
I place my cursor over the symbol defmacro and issue the keyboard-command M-. (slime-edit-definition).
This should bring me to the definition of defmacro.
But it doesn't.
It brings me here:
Why does it do this & how do I fix this
Notice there is a warning in the REPL when trying to find the source of DEFUN:
WARNING: inconsistent 2 form-number-translations
You can replicate it yourself in the REPL:
CL-USER> (let ((slynk::*buffer-package* (find-package :cl))
(slynk::*buffer-readtable* *readtable*))
(slynk:find-definitions-for-emacs "DEFUN"))
WARNING: inconsistent 2 form-number-translations
(("(DEFMACRO DEFUN)"
(:LOCATION (:FILE "/home/chris/data/src/sbcl/src/code/macros.lisp")
(:POSITION 4140)
(:SNIPPET "(setq doc nil)
(let* (;; stuff shared between LAMBDA and INLINE-LAMBDA and NAMED-LAMBDA
(lambda-guts `(,#decls (block ,(fun-name-block-name name) ,#forms)))
(lambda `(lambda ,lambda-list ,#lambda-guts))
(named-lambda `("))))
To find where the warning comes from, you could do as I did first and do a textual search on the repository, or you could use the following alternate method that works better, namely invoke the debugger on warnings:
(handler-bind ((warning (lambda (c) (invoke-debugger c))))
(let ((slynk::*buffer-package* (find-package :cl))
(slynk::*buffer-readtable* *readtable*))
(slynk:find-definitions-for-emacs "DEFUN")))
This comes from SLYNK-SBCL::FORM-NUMBER-POSITION, and the interesting value in the debugger is the source location obtained from SBCL:
#<SB-INTROSPECT:DEFINITION-SOURCE {10369C50F3}>
--------------------
The object is a STRUCTURE-OBJECT of type SB-INTROSPECT:DEFINITION-SOURCE.
PATHNAME: #P"SYS:SRC;CODE;MACROS.LISP"
FORM-PATH: (5)
FORM-NUMBER: 89
CHARACTER-OFFSET: 3917
FILE-WRITE-DATE: 3825178034
PLIST: NIL
DESCRIPTION: NIL
It says the source is the fifth toplevel form in the file (which corresponds to the character offset), and from here, the FORM-NUMBER is the 89th form in a depth-first search walk of the form (this comes from the structure's docstring).
But, if I recompile the function FORM-NUMBER-POSITION with DEBUG set to 3, the toplevel form read at this position, TLF is NIL:
1: (SLYNK-SBCL::FORM-NUMBER-POSITION #S(SB-INTROSPECT:DEFINITION-SOURCE :PATHNAME #P"SYS:SRC;CODE;MACROS.LISP" :FORM-PATH (5) :FORM-NUMBER 89 :CHARACTER-OFFSET 3917 :FILE-WRITE-DATE 3825178034 :PLIST NIL..
Locals:
DEFINITION-SOURCE = #S(SB-INTROSPECT:DEFINITION-SOURCE :PATHNAME #P"SYS:SRC;CODE;MACROS.LISP" :FORM-PATH (5) :FORM-NUMBER 89 :CHARACTER-OFFSET 3917 :FILE-WRITE-DATE 3825178034 :PLIST NIL :DESCRIPTION NIL)
FORM-NUMBER = 89
PATH-TABLE = #((0 0))
POS-MAP = #<HASH-TABLE :TEST EQ :COUNT 126 {103B227EA3}>
POS-MAP#1 = #<HASH-TABLE :TEST EQ :COUNT 126 {103B227EA3}>
STREAM = #<SB-IMPL::STRING-INPUT-STREAM {7F3E0350D953}>
TLF = NIL
TLF#1 = NIL
TLF-NUMBER = 5
In read-source-form, you can see that the form is being read inside a (ignore-errors (read ...)) form, which returns NIL in case of error. I tried calling (read ...) only but this somehow did not invoke the debugger, so I did the same thing as above and explicitly invoked it on any condition.
There is an error, namely that the package "SB-XC" does not exist, which is expected since, if I am not mistaken, this is a package that only exists during the compilation of SBCL itself.
I think you should contact the SBCL SLY developers and file a bug for this directly, they would certainly have a better idea of how to fix the behaviour (feel free to link to your question in addition to giving the usual details of the bug report).
I'm seeing pretty much what you're seeing.
Defun (line 280 of defboot.lisp) is a macro, which is defined in terms of defun-expander (line 230 of defboot.lisp) which is what you're seeing.
Whereas, defmacro takes you directly to its definition (line 15 of defmacro.lisp) which is what you're seeing.
It seems to be doing useful things.
I defined a new function 'addmore'
(defun addmore (x y z)
(testfn x (testfn y z)))
I compiled it all, and M-. on 'addmore' takes me to the definition of testfn.
So I think it's all working.
I'm trying to create a macro that makes a Clojure deftypeand which requires type hints to be generated. I currently have some test code:
(defmacro test-macro [n]
(let [obj-sym (gensym "obj")
p0 (with-meta 'p0 {:tag java.lang.Object})
p1 (with-meta 'p1 {:tag java.lang.Integer/TYPE})
r0 (with-meta 'remove {:tag java.lang.Boolean/TYPE})
r1 (with-meta 'remove {:tag java.lang.Object})]
`(deftype ~n [~obj-sym]
java.util.List
(~r0 [_ ~p0] (.remove ~obj-sym ~p0))
(~r1 [_ ~p1] (.remove ~obj-sym ~p1)))))
When it returns:
(test-macro test-it)
;clojure.lang.Compiler$CompilerException: java.lang.IllegalArgumentException: Must hint overloaded method: remove, ...
As a guide, it should produce something equivalent to:
(clojure.core/deftype ThisWorks [obj-5546]
java.util.List
(#^"boolean" remove [_ ^java.lang.Object p0-object] (.remove obj-5546 p0-object))
(^{:tag "java.lang.Object"} remove [_ ^int p0-int] (.remove obj-5546 p0-int)))
It looks like I am type hinting the wrong thing, or the meta data isn't being passed. Other than a fix for the immediate problem, bonus points if you can help out with the more general "meta" problem: how to debug a macro which manipulates metadata as macroexpand isn't very useful here..
Thanks
The :tag metadata is supposed to be a symbol, not a class object (since, after all, a symbol is all you can type as code in a non-macro situation: I can't embed the Class object int itself in my code!). So rather than Integer/TYPE, you just want 'int, and similarly for all your other typehints.
Here are two simple functions that use push on a variable passed in:
(defun push-rest (var) (push 99 (rest var)))
and
(defun just-push (something) (push 5 something))
The first one will permanently mutate the var passed. The second does not. This is quite confusing for someone who is learning the scoping behavior of this language:
CL-USER> (defparameter something (list 1 2))
SOMETHING
CL-USER> something
(1 2)
CL-USER> (just-push something)
(5 1 2)
CL-USER> something
(1 2)
CL-USER> (push-rest something)
(99 2)
CL-USER> something
(1 99 2)
In push-rest why isn't the var's scope local to the function like in just-push, when they are both using the same function, push?
According to Peter Siebel's Practical Common Lisp, Chapter 6. Variables: This might help you a lot:
As with all Common Lisp variables, function parameters hold object references. Thus, you can assign a new value to a function parameter within the body of the function, and it will not affect the bindings created for another call to the same function. But if the object passed to a function is mutable and you change it in the function, the changes will be visible to the caller since both the caller and the callee will be referencing the same object.
And a footnote:
In compiler-writer terms Common Lisp functions are "pass-by-value." However, the values that are passed are references to objects.
(Pass by value also essentially means copy; but we aren't copying the object; we are copying the reference/pointer to the object.)
As I noted in another comment:
Lisp doesn't pass objects. Lisp passes copies of object references to functions. Or you could think of them as pointers. setf assigns a new pointer created by the function to something else. The previous pointer/binding is not touched. But if the function instead operates on this pointer, rather than setting it, then it operates on the original object the pointer points too. if you are a C++ guy, this might make much more sense for you.
You can't push on a variable passed. Lisp does not pass variables.
Lisp passes objects.
You need to understand evaluation.
(just-push something)
Lisp sees that just-push is a function.
Now it evaluates something. The value of something is a list (1 2).
Then it calls just-push with the single argument (1 2).
just-push will never see the variable, it does not care. All it gets are objects.
(defun push-rest (some-list) (push 99 (rest some-list)))
Above pushes 99 onto the rest, a cons, of the list passed. Since that cons is visible outside, the change is visible outside.
(defun just-push (something) (push 5 something))
Above pushes 5 to the list pointed to by something. Since something is not visible outside and no other change has made, that change is not visible outside.
push works differently when it's passed a symbol or list as it's second argument. Pehaps you might understand it better if you do macroexpand on the two different.
(macroexpand '(push 99 (rest var)))
;;==>
(let* ((#:g5374 99))
(let* ((#:temp-5373 var))
(let* ((#:g5375 (rest #:temp-5373)))
(system::%rplacd #:temp-5373 (cons #:g5374 #:g5375)))))
Now most of this is to not evaluate the arguments more than once so we can in this case rewrite it to:
(rplacd var (cons 99 (rest var)))
Now this mutates the cdr of var such that every binding to the same value or lists that has the same object in it's structure gets altered. Now lets try the other one:
(macroexpand '(push 5 something))
; ==>
(setq something (cons 5 something))
Here is creates a new list starting with 5 and alters the local functions binding something to that value, that in the beginning pointed to the original structure. If you have the original structure in a variable lst it won't get changed since it's a completely different binding than something. You can fix your problem with a macro:
(defmacro just-push (lst)
(if (symbolp lst)
`(push 5 ,lst)
(error "macro-argument-not-symbol")))
This only accepts variables as argument and mutates it to a new list having 5 as it's first element and the original list as it's tail. (just-push x) is just an abbreviation for (push 5 x).
Just to be clear. In an Algol dialect the equivalent code would be something like:
public class Node
{
private int value;
private Node next;
public Node(int value, Node next)
{
this.value = value;
this.next = next;
}
public static void pushRest(Node var)
{
Node n = new Node(99, var.next); // a new node with 99 and chained with the rest of var
var.next = n; // argument gets mutated to have new node as next
}
public static void justPush(Node var)
{
var = new Node(5, var); // overwrite var
System.out.print("var in justPush is: ");
var.print();
}
public void print()
{
System.out.print(String.valueOf(value) + " ");
if ( next == null )
System.out.println();
else
next.print();
}
public static void main (String[] args)
{
Node n = new Node( 10, new Node(20, null));
n.print(); // displays "10 20"
pushRest(n); // mutates list
n.print(); // displays "10 99 20"
justPush(n); // displays "var in justPush is :5 10 99 20"
n.print(); // displays "10 99 20"
}
}
(push item place)
It work as follows when the form is used to instruct the place where this is referred to in the setf:
(setf place (cons item place))
Basedon your profile, it looks like you have familiarity with C-like languages. push is a macro, and is the following equivalence is roughly true (except for the fact that this would cause x to be evaluated twice, whereas push won't):
(push x y) === (setf x (list* x y))
That's almost a C macro. Consider a similar incf macro (CL actually defines an incf, but that's not important now):
(incf x) === (setf x (+ 1 x))
In C, if you do something like
void bar( int *xs ) {
xs[0] = xs[0] + 1; /* INCF( xs[0] ) */
}
void foo( int x ) {
x = x + 1; /* INCF( x ) */
}
and have calls like
bar(zs); /* where zs[0] is 10 */
printf( "%d", zs[0] ); /* 11, not 10 */
foo(z); /* where z is 10 */
printf( "%d", z ); /* 10, not 11 */
The same thing is happening in the Lisp code. In your first code example, you're modifying contents of some structure. In your second code example, you're modifying the value of lexical variable. The first you'll see across function calls, because the structure is preserved across function calls. The second you won't see, because the lexical variable only has lexical scope.
Sometimes I wonder if Lisp aficionados (myself included) promote the idea that Lisp is different so much that we confuse people into thinking that nothing's the same.
I'm trying to schedule a Clojure function using Executors.newSingleThreadScheduledExecutor(). The annoyance is that calling .get() on the resulting ScheduledFutureTask returns nil instead of the function's result.
I'm taking Mr Hickey's implementation of futures as the model.
(ns my.ns
(:import (java.util.concurrent Executors ThreadFactory TimeUnit)))
(def ^:private thread-pool-scheduler (atom nil))
(def ^:private thread-pool-counter (agent 0))
(defn- get-and-increment-thread-id []
(-> thread-pool-counter (send inc) deref))
(def ^:private thread-factory
(reify ThreadFactory
(newThread [this f]
(let [thread (Thread. f)]
(.setName thread (format "clojure-scheduled-future-thread-pool-%d"
(get-and-increment-thread-id)))
thread))))
(defn scheduled-future-call [^Callable f ^long delay ^TimeUnit unit]
(.schedule (scheduled-futures-executor) (bound-fn* f) delay unit))
(defn start-scheduled-futures-executor! []
(reset! thread-pool-scheduler
(Executors/newSingleThreadScheduledExecutor thread-factory)))
(defn scheduled-futures-executor []
(or #thread-pool-scheduler
(start-scheduled-futures-executor!)))
Everything works, and side-effects fire when appropriate (e.g. scheduling #(println "ok")).
However, calling the get() method of the resulting ScheduledFutureTask always gives me nil
(e.g. scheduling #(+ 5 5)).
I tried extending Callable explicitly, tried omitting bound-fn*, but the result is the same:
(defprotocol ISchedule
(schedule [this delay time-unit]))
(extend Callable
ISchedule
{:schedule (fn [this delay time-unit]
(.schedule (scheduled-futures-executor) this delay time-unit))})
My intuition is that the ScheduledExecutorService is choosing schedule(Runnable, long, TimeUnit) over schedule(Callable, long, TimeUnit), but shouldn't type hints fix that?
Many, many thanks for any help or tips!
My intuition is that the ScheduledExecutorService is choosing schedule(Runnable, long, TimeUnit) over schedule(Callable, long, TimeUnit),
I think you're right, only I reified Callable and it worked properly. (I also added Callable to the list of classes in the import statement).
EDIT: I also removed the call to bound-fn*
Check it out:
(defn make-callable [f] (reify Callable (call [this] (f))))
(def cat (make-callable (fn [] (println "meeow") "i am a cat")))
(def who-are-you? (scheduled-future-call cat 2 TimeUnit/SECONDS))
(println "Tell me who you are!\n\t" (.get who-are-you?))
outputs:
meeow
Tell me who you are!
i am a cat
The ^Callable hint on f does you no good, because you just call bound-fn anyway, whose result is not type-hinted. You need to hint the thing you actually pass to .schedule. More importantly, you need to hint the target object itself (the executor), as the compiler will (quite reasonably) ignore any hints on arguments if the target object is not hinted: it has to do reflection anyway if it doesn't know the type of the target!
So, hint both of those things in a let-binding1 and you should be fine. You might need to hint all the rest of the arguments as well for disambiguation, but I don't think so.
1 Note: do not hint the expressions generating them, eg ^Callable (bound-fn f). That usually works, but sometimes doesn't, in scenarios that are complicated to explain. Best to just avoid that scenario.
The macro, transform!, as defined below seems to work for => (transform! ["foo" 1 2 3]). The purpose is to take in a list, with the first element being a string that represents a function in the namespace. Then wrapping everything into swap!.
The problem is that transform! doesn't work for => (transform! coll), where (def coll ["foo" 1 2 3]). I am getting this mystery exception:
#<UnsupportedOperationException java.lang.UnsupportedOperationException: nth not supported on this type: Symbol>
The function:
(defmacro transform!
" Takes string input and update data with corresponding command function.
"
[[f & args]] ;; note double brackets
`(swap! *image* ~(ns-resolve *ns* (symbol f)) ~#args))
I find it strange that it works for one case and not the other.
Macros work at compile-time and operate on code, not on runtime data. In the case of (transform! coll), the macro is being passed a single, unevaluated argument: the symbol coll.
You don't actually need a macro; a regular function will suffice:
(defn transform! [[f & args]]
(apply swap! *image* (resolve (symbol f)) args)))
Resolving vars at runtime could be considered a code smell, so think about whether you really need to do it.
You're passing a symbol to the macro, namely coll. It will try to pull that symbol apart according to the destructuring statement [f & args], which won't be possible of course.
You can also use (resolve symbol) instead of (ns-resolve *ns* symbol).