*`defprotocol` is a top-level form...*
This is interesting, and it's something I've wondered about. As far as I
can tell, there's really no distinction between top-level forms and other
forms, for example this is legal and works:
(defn define-my-functions []
(defn test-1 []
1)
(defn test-2 []
2))
=> #=(var plugin.performance.project/define-my-functions)
test-1
=> Unbound: #'plugin.performance.project/test-1
test-2
=> Unbound: #'plugin.performance.project/test-2
(define-my-functions)
=> #=(var plugin.performance.project/test-2)
test-1
=> plugin.performance.project$define_my_functions$test_1__3112@30e63c09
test-2
=> plugin.performance.project$define_my_functions$test_2__3114@24ae6e0a
Given this, are there any forms that are genuinely top-level from the
compiler's point of view? I'm assuming defining functions like this is
generally discouraged, but defining them inside of let-forms seems
relatively common:
(let [a 1]
(defn get-a []
a))
=> #=(var plugin.performance.project/get-a)
(get-a)
=> 1
On 6 June 2013 09:57, Stuart Sierra <[email protected]> wrote:
> Hi Vincent,
>
> `defprotocol` is a top-level form, not really meant to be mixed with
> value-returning expressions like `fn`. Protocols are always global because
> of how they compile into Java interfaces.
>
> Here's one way to make it work, by defining a symbol instead of returning
> a function:
>
> (defmacro create-protocol [protocol symbol implementation]
>
> (let [[protocol-name signature] protocol]
> `(do
> (defprotocol ~protocol-name ~signature)
> (defn ~symbol [] (reify ~protocol-name ~implementation)))))
>
>
> (create-protocol [P (method [this])]
> constructor
>
> (method [this] (println "method")))
>
> (method (constructor))
>
> -S
>
>
>
> On Wednesday, June 5, 2013 9:16:05 AM UTC-4, Vincent wrote:
>>
>> I’m trying to write a macro that defines a protocol and a function that,
>> when called, returns an implementation of that protocol.
>>
>> I’ve reduced the code to the following example:
>> (defmacro create-protocol [protocol implementation]
>> (let [[protocol-name signature] protocol]
>> `(do
>> (defprotocol ~protocol-name ~signature)
>> (fn [] (reify ~protocol-name ~implementation)))))
>>
>> (let [f (create-protocol [P (method [this])]
>> (method [this] (println "method")))]
>> (method (f)))
>>
>>
>> The original code is more complicated, where the function will read a
>> value from a file and, depending on that value, return the appropriate
>> implementation of the protocol.
>>
>> When I run Clojure 1.5.1 on that code I get the following exception:
>> Exception in thread "main" java.lang.**NullPointerException,
>> compiling:(protocol.clj:7:9)
>> at clojure.lang.Compiler.**analyzeSeq(Compiler.java:6567)
>> at clojure.lang.Compiler.analyze(**Compiler.java:6361)
>> at clojure.lang.Compiler.**analyzeSeq(Compiler.java:6548)
>> at clojure.lang.Compiler.analyze(**Compiler.java:6361)
>> at clojure.lang.Compiler.analyze(**Compiler.java:6322)
>> at clojure.lang.Compiler$**BodyExpr$Parser.parse(**
>> Compiler.java:5708)
>> at clojure.lang.Compiler$**FnMethod.parse(Compiler.java:**5139)
>> at clojure.lang.Compiler$FnExpr.**parse(Compiler.java:3751)
>> at clojure.lang.Compiler.**analyzeSeq(Compiler.java:6558)
>> at clojure.lang.Compiler.analyze(**Compiler.java:6361)
>> at clojure.lang.Compiler.**analyzeSeq(Compiler.java:6548)
>> at clojure.lang.Compiler.analyze(**Compiler.java:6361)
>> at clojure.lang.Compiler.analyze(**Compiler.java:6322)
>> at clojure.lang.Compiler$**BodyExpr$Parser.parse(**
>> Compiler.java:5708)
>> at clojure.lang.Compiler.**analyzeSeq(Compiler.java:6560)
>> at clojure.lang.Compiler.analyze(**Compiler.java:6361)
>> at clojure.lang.Compiler.**analyzeSeq(Compiler.java:6548)
>> at clojure.lang.Compiler.analyze(**Compiler.java:6361)
>> at clojure.lang.Compiler.access$**100(Compiler.java:37)
>> at clojure.lang.Compiler$LetExpr$**Parser.parse(Compiler.java:**5973)
>> at clojure.lang.Compiler.**analyzeSeq(Compiler.java:6560)
>> at clojure.lang.Compiler.analyze(**Compiler.java:6361)
>> at clojure.lang.Compiler.analyze(**Compiler.java:6322)
>> at clojure.lang.Compiler$**BodyExpr$Parser.parse(**
>> Compiler.java:5708)
>> at clojure.lang.Compiler$**FnMethod.parse(Compiler.java:**5139)
>> at clojure.lang.Compiler$FnExpr.**parse(Compiler.java:3751)
>> at clojure.lang.Compiler.**analyzeSeq(Compiler.java:6558)
>> at clojure.lang.Compiler.analyze(**Compiler.java:6361)
>> at clojure.lang.Compiler.eval(**Compiler.java:6616)
>> at clojure.lang.Compiler.load(**Compiler.java:7064)
>> at clojure.lang.Compiler.**loadFile(Compiler.java:7020)
>> at clojure.main$load_script.**invoke(main.clj:294)
>> at clojure.main$script_opt.**invoke(main.clj:356)
>> at clojure.main$main.doInvoke(**main.clj:440)
>> at clojure.lang.RestFn.invoke(**RestFn.java:408)
>> at clojure.lang.Var.invoke(Var.**java:415)
>> at clojure.lang.AFn.**applyToHelper(AFn.java:161)
>> at clojure.lang.Var.applyTo(Var.**java:532)
>> at clojure.main.main(main.java:**37)
>> Caused by: java.lang.NullPointerException
>> at clojure.lang.Compiler.**resolveIn(Compiler.java:6840)
>> at clojure.lang.Compiler.resolve(**Compiler.java:6818)
>> at clojure.lang.Compiler$**NewInstanceExpr.build(**
>> Compiler.java:7427)
>> at clojure.lang.Compiler$**NewInstanceExpr$ReifyParser.**
>> parse(Compiler.java:7377)
>> at clojure.lang.Compiler.**analyzeSeq(Compiler.java:6560)
>> ... 38 more
>>
>> From what I understood by tracing through the compiler it seems that the
>> P var is created at macro expansion time but bound at execution time only.
>> When expanding the ‘reify’ macro, P is still unbound, which yields a nil
>> interface, which triggers the NPE.
>>
>> I could solve the problem by redefining the macro in the following way:
>> (defmacro create-protocol [protocol implementation]
>> (let [[protocol-name signature] protocol]
>> (eval `(defprotocol ~protocol-name ~signature))
>> `(fn [] (reify ~protocol-name ~implementation))))
>>
>> Using eval doesn’t feel right though.
>>
>> I guess I could modify my code to avoid using protocols, but it seemed to
>> me to be the most natural way of achieving what I wanted.
>>
>> I was just wondering if anybody had any opinion or suggestion about that.
>> Am I going off track? Is there a more idiomatic way of doing things that I
>> missed?
>>
>> Thanks,
>> Vincent
>>
> --
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to [email protected]
> Note that posts from new members are moderated - please be patient with
> your first post.
> To unsubscribe from this group, send email to
> [email protected]
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
> ---
> You received this message because you are subscribed to the Google Groups
> "Clojure" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> For more options, visit https://groups.google.com/groups/opt_out.
>
>
>
--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.