Yes, it prevents typos to go unnoticed. You can write a forward
declaration :
(declare check-services); equivalent to (def check-services)
(defn main []
(check-services))
(defn check-services []
1)
HTH
Christophe
--
Professional: http://cgrand.net/ (fr)
On Clojure: http://clj-me.blogspot.com/ (en)
Yes. Clojure checks that symbols are defined before they are used.
You can put
(declare check-services)
before your definition of main to define the symbol and make it clear
to the (human) reader that you indeed to give the real definition later.
Konrad.
Am 16.03.2009 um 18:36 schrieb Elena:
> IMHO, this is a no-no for interactive development. I understand that
> it helps avoiding undefined symbols, but such code integrity checks
> could be delayed to a final compilation stage. Having them earlier
> forces you to develop code in a bottom-up way.
I don't think so. I start with a function. *hackhackhack*
Then I realise, that some part should be extracted into
a helper function, eg. to share code with third function.
Then it is matter of convention of the local project, whether
this helper function is place in front or after the other functions
combined with a declare.
I use a top-down approach all the time and it works
pretty well in Clojure.
Sincerely
Meikel
I may be missing something, but how does having to (declare) vars fix typos? I don't think anyone is suggesting *creating* a var that is referenced before it is defined. What people are asking for is that the compiler looks-ahead to verify that the var will eventually be defined, and then go on its merry way. Typos would still be discovered, and people wouldn't have to stop and (declare).
On Mon, Mar 16, 2009 at 4:08 PM, Paul Stadig <pa...@stadig.name> wrote:I may be missing something, but how does having to (declare) vars fix typos? I don't think anyone is suggesting *creating* a var that is referenced before it is defined. What people are asking for is that the compiler looks-ahead to verify that the var will eventually be defined, and then go on its merry way. Typos would still be discovered, and people wouldn't have to stop and (declare).
Yeah I wasn't suggesting that vars should be created, sorry if it sounded like I was (I mentioned declare because this came up in another thread about declare and someone had hacked the reader to not bail immediately on undefined symbols).
In CL, if you have definitions out of order in the compiler will issue warnings.
I'm not saying it's an easy change...
Paul
Sometimes, I don't understand you guys. You claim that you have
to develop bottom-up, when in fact you do top-down. I really don't
see the problem with the current behaviour...
Sincerely
Meikel
> But it also protects you from typos. And this can be even more
> important. Imagine you have a complex program and accidently
> made a typo, and this will go unnoticed for days and days, until
> the program actually runs your code...
If you go days and days without actually running your code, then you
deserve what you get. A test suite would catch this for you every time;
developing without one is irresponsible.
-Phil
Sincerely
Meikel
geeze people, i'm tired of the "tests are the answer to everything."
give it a break. not every test suite will be perfect from day one,
and what i wrong with defense in depth?
said by a person who likes statically typed, inferred, languages. :-)
Am 16.03.2009 um 22:44 schrieb Laurent PETIT:
> In french, we have a sentence for that, that would translate
> literally into "eat the banana by both ends at the same time".
>
> I don't think top-down and bottom-up programming are antogonists. I
> often do both at the same time myself.
According to Paul Graham, this is a one aspect of Lisp:
working both ways at the same time. Top-down for the
program logic and bottom-up by building small helpers
and macros.
My remark was pointed at the fact, that before it was
claimed, that the one way doesn't work in Clojure and
one has to go the other. And then the same person
goes on to contradict him(or her?)self. But be it...
To say something more constructive for the discussion:
here Rich's original answer on the matter:
Sincerely
Meikel
-Phil
> OK, so now is time for another ugly english translation of a french
> proverb "before saying something, roll your tongue 8 times in your
> mouth before speaking". I guess this could also be applied for e-mail
Agreed, it could have been worded better.
But keep in mind the context: it was suggested that certain problems
would go unnoticed for days because the code was loaded never run. I
think this is not a concern; you might as well say these problems could
go unnoticed because the code is never loaded.
> Seriously, treating a very large audience of people as "irresponsible"
> (including Rich himself, the author of clojure) does not seem a very
> "responsible" behavior, does it ? :-)
Clojure has a test suite. It lives in contrib. Bugs in Clojure have been
committed even though they were entirely avoidable, simply because the
test suite wasn't being used. This strikes me as an irresponsible
development practice. It's not horrible, but it wastes peoples' time.
> But please, think about it twice before saying people are
> irresponsible. Unit tests are not the only answer to bug-free and
> quality programs.
Sure, I don't mean that this reflects on peoples' character. I only wish
to judge the practice, not the practitioner.
But for things like clojure, the alternative is to perform those same
tests manually. I don't think it's a good use of humans' time to perform
tasks that could be easily done by a machine.
> And please note that when talking about tests, I did not tell if they
> had to be automatic or not. I think that it's not a natural conclusion
> to say that tests must be automated. To me, it's rather a
> cost/benefits balance that must leads to the conclusion that automatic
> unit tests are good or not for your project.
That's true; certain types of programs (particularly GUIs and JS-heavy
cross-browser web applications) are extremely difficult to automate
tests for. But this is the exception rather than the rule.
-Phil
Sure, I don't mean that this reflects on peoples' character. I only wish
> But please, think about it twice before saying people are
> irresponsible. Unit tests are not the only answer to bug-free and
> quality programs.
to judge the practice, not the practitioner.
Not at all. The latter is far more common, especially in online discussions.
And to say that people deserve the problems that come upon them, that
was harsh of me. I rather should have said that they should expect to
have problems like that.
-Phil
Am 16.03.2009 um 23:39 schrieb Elena:
> - the REPL could allow for an option to just warn about missing
> definitions;
> - when loading/compiling a file, Clojure could parse all definitions
> before complaining about missing ones.
>
> It seems that such a solution would offer both maximum flexibility
> when prototyping and maximum sanity checking when finalizing the code.
> What do you think?
I probably wouldn't notice such a change. I code in
the repl only very small snippets. To check my
expectations of some return value of a function or
the like. (Basically poor man's debugger)
Things like a function I write in a file and send them
to the clojure server, and only call them in the repl.
I almost never write a function like this.
(defn foo [] (let [x (bar)] (inc x)))
"Hmmm... So how might bar look like?"
It's almost always the other way around:
(defn foo [] (let [x (long ugly stuff)] (inc x)))
"Iek. I should refactor this."
Hmm... So maybe I don't do prototyping....
Sincerely
Meikel
Just FYI,
The actual patch is in the files section:
http://groups.google.com/group/clojure/web/auto-def.patch
With an example:
http://groups.google.com/group/clojure/web/lit-wc.clj
From a longer thread about 'snake' which talked about literate
programming:
http://groups.google.com/group/clojure/browse_thread/thread/2a23a95bf22938a3
It does not implement existence checking upon completion of compile
unit. How exactly should this work? We can keep a set of symbols auto-
defined and 'at checkpoint' test if they are still undefined...
however what is the 'checkpoint'? For fully compiled code the
'checkpoint' is clear - but Clojure is dynamic... what should happen
with this code:
(defn myfun []
(another-fun 5))
(myfun)
(defn another-fun [x]
(inc x))
In a compiled language that would be valid, but in a dynamic language
it is a runtime error.
I don't see how to overcome this, which makes
auto-def quite useless :)
> Furthermore, if I understand Rich's opinion in that regards, this
> code:
>
> (defn myfun []
> (another-fun 5))
>
> should be rejected if definition of "another-fun" is missing (but I'd
> say: only in code to be released).
I don't mind the current behavior of requiring a var to be def'd
before it's referenced. I do find the idea of relaxing that
requirement interesting, though, so I thought about one possible way
to accomplish it.
To support auto-defining of unresolved symbols, either at the repl or
in loaded files, Clojure could provide a var:
*autodef-unresolved-symbols*
When true, any symbol references that would have triggered the "unable
to resolve symbol" exception would instead be auto-def'd as unbound
and resolved. (Note that since they would be unbound, they still could
not be dereferenced which is cool.)
The repl could allow this to be set!-able.
If *autodef-unresolved-symbols* is true, and if "a" has never been
mentioned before, this interaction with Clojure:
user=> a
java.lang.Exception: Unable to resolve symbol: a in this context
would become:
user=> a
java.lang.IllegalStateException: Var user/a is unbound.
When a symbol is auto-def'd (presumably in ns-resolve, or in the Java
code that supports it), the corresponding resolved symbol would be
added to an internal ref (bound to a set):
*autodefs*
One could retrieve the names of any (currently/still) unbound
autodef'd vars via a call:
(unbound-autodefs)
This call would first update the value bound to the ref *autodefs* by
removing any of its entries that now have root bindings and return its
value after the update. In this way, updating the set of unbound
autodefs is lazy and not done until someone is interested.
For the case of loading files, load could be wrapped with something
like:
(binding [*autodef-unresolved-symbols* true *autodefs* (ref #{})]
(load...)
(when-let unbound (seq (unbound-autodefs))
(throw Exception. (str "The following vars were not defined: "
unbound))))
Under this system:
- the behavior is optional at the repl. anybody not using it sees no
change in behavior there
- Clojure would relax its ordering requirements in source files while
still ensuring that all forward references are satisfied by the end of
each file.
- any vars that are intended to be unbound would (still) have to be
def'd explicitly.
This system seems workable and desirable to me, but I gather that
Common Lisp packages have some subtle and sometimes undesirable
behaviors associated with a similar capability. I don't know whether
other Clojure goodness would make any such issues unimportant in
practice or if they would still be a problem.
--Steve
Hi Laurent,
> Your solution is indeed close to what I had in mind in terms of
> requirements. I was currently hacking with clojure java class
> Compiler to enhance Timothy's patch and add a variation on what you
> described.
Cool. I look forward to seeing what you come up with.
> * does the Compiler.load() process happen all in the same thread ?
> (And if yes, is that a guaranteed feature, or could that change in
> the future ?)
Compiler.load() appears to execute in a single thread currently. I'm
not aware of any guarantee about that. As a general rule, anything
that's not documented by Rich is eligible to be changed in the future.
In thinking about how that might work, it seems to me that making a
single load multithreaded would be more complex than it would be
worth, but no guarantees.
> * How do you correctly deref, ref-set! from java code by directly
> using a Var instance ?
ref-set is an operation on refs. deref can be applied to refs.
myRef.set(anObject);
myRef.deref();
Or, using the fine clojure.contrib.repl-utils:
user=> (source ref-set)
(defn ref-set
"Must be called in a transaction. Sets the value of ref.
Returns val."
[#^clojure.lang.Ref ref val]
(. ref (set val)))
nil
user=> (source deref)
(defn deref
"Also reader macro: @ref/@agent/@var/@atom/@delay/@future. Within a
transaction,
returns the in-transaction-value of ref, else returns the
most-recently-committed value of ref. When applied to a var, agent
or atom, returns its current state. When applied to a delay, forces
it if not already forced. When applied to a future, will block if
computation not complete"
[#^clojure.lang.IDeref ref] (.deref ref))
nil
user=> (show clojure.lang.Ref)
=== public clojure.lang.Ref ===
[ 0] <init> (Object)
...
[10] deref : Object ()
...
[46] set : Object (Object)
...
[53] wait : void (long,int)
nil
user=> (show clojure.lang.Ref 46)
#<Method public java.lang.Object
clojure.lang.Ref.set(java.lang.Object)>
user=> (show clojure.lang.Ref 10)
#<Method public java.lang.Object clojure.lang.Ref.deref()>
user=>
--Steve
On Mar 16, 2009, at 8:25 PM, Laurent PETIT wrote:
Hi Laurent,Cool. I look forward to seeing what you come up with.
Your solution is indeed close to what I had in mind in terms of requirements. I was currently hacking with clojure java class Compiler to enhance Timothy's patch and add a variation on what you described.
Compiler.load() appears to execute in a single thread currently. I'm not aware of any guarantee about that. As a general rule, anything that's not documented by Rich is eligible to be changed in the future. In thinking about how that might work, it seems to me that making a single load multithreaded would be more complex than it would be worth, but no guarantees.
* does the Compiler.load() process happen all in the same thread ? (And if yes, is that a guaranteed feature, or could that change in the future ?)
ref-set is an operation on refs. deref can be applied to refs.
* How do you correctly deref, ref-set! from java code by directly using a Var instance ?
> The real one was : how to correctly get the thread bound value, and
> change the thread bound value of a Var instance, from within java
> code ?
You can set the thread-bound value with "set" (defined in Var.java).
You get the thread-bound value with "get" or "deref" (also in Var.java).
Note that as with a deref in Clojure, if a var has no thread local
binding, "get" will return its root binding or throw an exception if
it has none.
--Steve
--Steve
With autodef, one could have to either move the definition or add a
declare just to be able to add a type hint.
Christophe
Stephen C. Gilardi a écrit :
--
Professional: http://cgrand.net/ (fr)
On Clojure: http://clj-me.blogspot.com/ (en)
I understand why people want to be able to do things like this:
(defn b [] (a))
(defn a [] (...))
But that's completely different from the above. The above is more like:
(a)
(defn a [] (...))
Why would you want that to warn and not be an error? What would you
expect it to do if it was just a warning?
i.e. at the REPL, what would you expect that to print at each step.
user=> (a)
???
user=> (defn a [] (...))
#'user/a
--
Michael Wood <esio...@gmail.com>
I think you have been pretty clear right up until your previous e-mail
where you implied that:
(a)
(defn a ...)
should warn instead of error ;)
--
Michael Wood <esio...@gmail.com>
> How do you cope with type-hinted vars or macros? (ok #'declare does
> not
> work for macros neither)
>
> With autodef, one could have to either move the definition or add a
> declare just to be able to add a type hint.
The class of vars you're talking about are vars whose metdata and/or
value is needed at compile time. autodef as I've described it would
not help with those. To operate properly, they would still need to be
declared and/or defined before code that uses them is compiled.
The common theme is that all vars must have complete definitions
before their metadata and/or value is used. The vars you mention are
used earlier than others.
I think there is the potential for a good deal of confusion in the
cases you mention. Macros that were autodef'd because calls to them
were compiled into other functions would be called from those sites if
they were ordinary functions. Compiled code for hinted vars would not
benefit from the hints.
On the other side of the argument, the situation with autodef is
exactly the same as a policy (implemented by hand) of declaring all
vars you wish to define out of order using un-hinted declares. Autodef
simply makes that process automatic.
A complete solution would include a provision like "require for
syntax" (and likely other kinds of require/loading) that would allow a
module system to supply the compiler with all it needs independent of
the ordering of functions within a module or modules within a system.
--Steve
> I wanted to say that entering:
>
> (defn a []
> (b))
>
> should issue a warning (and I don't think an exception is the right
> way to warn the user).
The autodef I described would not warn, nor throw an exception in this
case.
I think "warn on autodef" would be a good feature at the repl. If it
were controlled by a var (say, *warn-on-autodef*), it could be
optional at the repl and suppressed in the case of loading source files.
--Steve
I remain opposed, and here are some more reasons:
when you say
(def foo [x]
(bar x 42))
you are as likely to be referring to a forgot-to-require bar-lib as
you are to a to-be-defined bar.
The compiler can only default the latter, by interning bar in the
current namespace. There is no notion of compiling with a placeholder,
the name must be resolved to something. Should you really intend the
former, you end up with a mess, as you add (:require bar-lib) to your
ns call only to have it fail with a conflict - bar already defined in
your-ns.
Then you have to ns-unmap bar and retry. This is a big pain and one
you encounter all the time in Common Lisp, where the reader has auto-
interning characteristics similar to what is being discussed here.
Anything based on a unit-of-compilation concept has real problems for
Clojure, which generally treats a program as a stream of expressions.
Looking ahead is not an option, especially at the repl and other
streaming scenarios.
The biggest reason is - I'm not having the problems you are
describing, and neither are other experienced Clojure developers -
many of whom use the repl a lot.
On Mar 16, 2009, at 8:25 PM, Laurent PETIT wrote:
Hi Laurent,Cool. I look forward to seeing what you come up with.
Your solution is indeed close to what I had in mind in terms of requirements. I was currently hacking with clojure java class Compiler to enhance Timothy's patch and add a variation on what you described.
> I give up on this one. I've now been half convinced that it's even
> less an ideal solution than I expected at first, and I prefer use
> the little free time I have to enhance clojuredev.
Sounds good. Thanks for closing the loop on this.
--Steve