Legal but comparable to a hidden side effect with all the pitfalls that can 
derive
from this.

It's not a compiler issue, more a discipline issue.

Having a helper macro like Stuart suggest is acceptable to me
if it's called at top level, it's always done. There's less ambiguity.

Using def/defn in a function is less desirable, the definition/redefinition 
occurs when you
call the fn and it may occur at less desirable times or could be hard to spot.

If you need a local fn, let-fn is exactly for this purpose. The scope is limited
to the let-fn body.

Luc P.


> *`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.
> > > --
Softaddicts<[email protected]> sent by ibisMail from my ipad!

-- 
-- 
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.


Reply via email to