Disclaimers and Random Observations:
* This is not an official or final list -- intended as provocation for
discussion. If some of them aren't dead wrong then I didn't try hard
enough. :-)
* Rules are made to be broken, so whatever standards we come up with
will not be absolute.
* Putting my money where my mouth is: I am willing to dedicate
significant time to editing existing code to be in line with the
standards, once we agree on some.
* This document should (eventually) be useful for programmers
fairly new to Clojure. Links out to additional exposition is
probably the way to go.
The Standards:
* Get the name and signature right. Rich strongly respects Java's
commitment to not break existing code. In practice, that means we
can tweak the implementation forever, but once we publish a name
and signature we need to stick with it. (In practice I think this
means that we want many people to review the name and sig, even if
they don't review the implementation details.)
* Use type hints where appropriate - *warn-on-reflection* should not
bark at lib code.
* Use good names, and don't be afraid to collide with names in other
namespaces. That's what the flexible namespace support is there for.
* Be explicit and minimalist about dependencies on other
packages. (Prefer the :only option to use and require).
* Don't use a macro when a function can do the job. If a macro is
important for ease-of-use, expose the function version as
well. (Open question: should there be a naming convention here? A
lot of places use "foo" and "foo*".)
* If you are sure you have all the information at compile time, use a
macro where it would improve performance sensitive code. (The
discussion on a macro for logging [2] is instructive. We end up
needing both versions because the information is only *sometimes*
available at compile time.)
* Provide a library-level docstring.
* Provide automated tests with full branch coverage.
* Unroll optional arguments. That is
(foo x y :optional z)
not
(foo x y {:optional z})
--This one is from Rich.
* Use '?' suffix for predicates.
* Include a docstring. Except: If your function is so self-explanatory
that the docstring cannot do anything more than repeat the name and
arglist, then do not include a docstring. (Personally I would remove
some of the docstrings already in place.)
* When in doubt, expose the performant version. Clojure goes to great
lengths to enable performance when you need it, and lib should
too. (That's why we don't have multimethod + in core, for instance.)
Users can always create more polymorphic APIs on their own,
hijacking symbols if they want to.
* If you take a good name that collides with core, make sure your
semantics are parallel (possibly minus laziness). Good example of
this is string functions that shadow core seq functions.
* Make liberal use of assert and pre- and post- conditions. This is
not idiomatic today, but it should be going forward. See [1].
* Be lazy where possible.
* Follow clojure.core's example for idiomatic names like pred and
coll.
* Do NOT follow idioms from clojure.core's preamble code. That code
runs in a limited environment because Clojure is not bootstrapped
yet.
* Decompose the pieces. If your name isn't Rich, don't write a form as
long as, say, the definition of doseq.
* Use (:property foo) to access objects, but (foo :property) to reach
into collections. The difference communicates what you think foo is.
* Idiomatic code uses destructuring a lot. However, you should only
destructure in the arg list if you want to communicate the
substructure as part of the caller contract. Otherwise, destructure
in a first-line let. Example: my snake code from the book [3] fails
this test, doing too much destructuring in arg lists.
* Prefer updating over setting. Many reasons: the unified update model
provides a simple standard way to do this. Helps you discover
commutative operations. Reduces the surface area of assumptions you
are making about the object you are updating.
* Don't support operations on the wrong collection type. If your
algorithm is only performant with random access, then require an
arg that has random access.
* Use *earmuffs* only for things intended for rebinding.
* Use the bang! only for things not safe in an STM transaction.
* Prefer sequence-library composition over explicit loop/recur.
* Rebindable vars should be paired with scoping macros, e.g. *in* and
with-in-str.
* Lazy seqs should be exposed as functions that hold only the minimum
state needed, a.k.a. "let go of your head." Let the caller decide
how much local memory they want to use.
[1] http://www.assembla.com/spaces/clojure/tickets/250-turn-off-able-asserts--with-better-messages
[2] http://groups.google.com/group/clojure/browse_thread/thread/79c609df3632a5d1/40fbbacf3b2cc1e5?lnk=gst&q=log4j+not+detected#40fbbacf3b2cc1e5
[3] http://github.com/stuarthalloway/programming-clojure/commit/7758eac3b0434654902835fde47c854708538864
--
You received this message because you are subscribed to the Google Groups "Clojure Dev" group.
To post to this group, send email to cloju...@googlegroups.com.
To unsubscribe from this group, send email to clojure-dev...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/clojure-dev?hl=en.
2010/2/2 Sean Devlin <francoi...@gmail.com>:
> One addition:
>
> * Data goes last, when possible (e.g. the str-utils3 migration).
Not sure about that one. e.g. with prototypes data goes first.
> Design
> around ->>, comp & partial. Designing around -> is a last resort.
Not sure about that one. While I understand your appeal to point-free
style, I would not make this a general rule.
The general rule would better be expressed as : prefer code clarity
over compactness.
Strike one! :-)
For example, someone mentioned that repl-utils generates a lot of
reflection warnings, but the stuff defined there is never
intended to be used in critical paths. It seems to me type hints
sufficient to prevent warnings there would actually be an
anti-pattern, a demonstration of unnecessary over-hinting in code
that doesn't need it -- not something to be recommended to people
new to Clojure.
Perhaps we need a way to turn off reflection warnings on
a per-namespace basis or something? Or maybe just have 'load'
do:
(binding [*warn-on-reflection* *warn-on-reflection*] ...)
...so that it's safe to set it on and off as needed in .clj code
files?
> * Provide automated tests with full branch coverage.
Are we leaving behind the headstrong care-free days of our youth?
> * Follow clojure.core's example for idiomatic names like pred and
> coll.
You're referring to argument names here?
I like all the rest. Perhaps add:
* Use (Klass/static) (Klass.) and (.instance obj) interop styles,
with the only exception being in code-generating-code where the
older (. blah) style may be easier to produce.
It may not be necessary for contrib commiters, but I still see
a startling amount of new code in the wild that violates that
rule, so it may be worth mentioning explicitly.
Thanks for pulling this together.
--Chouser
> * Use type hints where appropriate - *warn-on-reflection* should not
> bark at lib code.
IMO, this collides with the recent moved towards not AOT-compiling
contrib -- if that doesn't happen regularly, then no one's going to
notice reflection warnings.
> * Unroll optional arguments. That is
> (foo x y :optional z)
> not
> (foo x y {:optional z})
> --This one is from Rich.
Could you clarify this, or point to an example? I'm not grokking at
the moment.
> * Make liberal use of assert and pre- and post- conditions. This is
> not idiomatic today, but it should be going forward. See [1].
I'd +1 this, with the caveat that preconditions should not be used for
verifying arguments to 'public' functions (since those assertions'
execution is at the behest/permission of whoever's running the code).
I'll refer here to the relevant Java-specific guidelines on assertions
in conjunction with public APIs: http://java.sun.com/j2se/1.5.0/docs/guide/language/assert.html
> * Use (:property foo) to access objects, but (foo :property) to reach
> into collections. The difference communicates what you think foo is.
I'd disagree here -- I always use (:property foo), whether foo is a
map or an object. IMO, (foo :property) should only be used to invoke
a defined fn foo.
> * Use *earmuffs* only for things intended for rebinding.
And I'd add use plus signs for things that are decidedly constant and
not intended to be rebound, like +speed-of-light+.
Cheers,
- Chas
Nope. It depends on what has been explicitly specified in the
"documentation part" of the public method : in the examples shown in
the above mentioned link, the method javadoc explictly states it will
(throw an exception if arg1 ...). But that's not preconditions for me,
that's "belt and strap" (is this an idomatic english expression ?). A
precondition would just be : "we expect the arg to respect conditions
x & y, ..., otherwise, undefined behaviour".
So I don't see why one could not check args in preconditions,
depending on how one has formulated it in the docstring.
>
>> * Use (:property foo) to access objects, but (foo :property) to reach
>> into collections. The difference communicates what you think foo is.
>
> I'd disagree here -- I always use (:property foo), whether foo is a map or
> an object. IMO, (foo :property) should only be used to invoke a defined fn
> foo.
>
>> * Use *earmuffs* only for things intended for rebinding.
>
> And I'd add use plus signs for things that are decidedly constant and not
> intended to be rebound, like +speed-of-light+.
>
> Cheers,
>
> - Chas
>
I'll take this golden opportunity to bring up let-kw again. :) It
makes this style of keyword argument parsing easy. Except now I think
it belongs somewhere other than c.c.macros.
http://www.assembla.com/spaces/clojure-contrib/tickets/51
inc
I guess one namespace could be just named c.c.lib, or c.c.core (in
this case, all existing stuff in c.c.core that is not yet relevant for
c.c.lib could be move somewhere else), and "hold" all utility
functions / macros that complement clojure.core.
Those utility functions/macros that are of very general utility and
currently scattered throughout c.c (or placed in namespaces which
technical names e.g. c.c.macros) could be migrated to c.c.core.
Stu
Hell yes. I only use (foo :property) when I'm really, really excited
about the possibility of a NullPointerException.
"The thing that isn't null" goes at the front.
I would change this to "think carefully about arg order, and be
consistent within a lib."
> One Contradiction:
>
> * DO support operations on the wrong collection type (e.g. random
> access). Indicate any constraints in the doc, but let me worry
> about that as a consumer of the lib.
Still not sold. Other votes.
> One Personal Request:
>
> Give me enough test cases, doc strings, and a use case or two, so I
> can create an episode for each namespace. Beginner education & all
> that.
Agreed.
Stu
>> * Use type hints where appropriate - *warn-on-reflection* should not
>> bark at lib code.
Chouser:
> Strike one! :-)
>
> For example, someone mentioned that repl-utils generates a lot of
> reflection warnings, but the stuff defined there is never
> intended to be used in critical paths. It seems to me type hints
> sufficient to prevent warnings there would actually be an
> anti-pattern, a demonstration of unnecessary over-hinting in code
> that doesn't need it -- not something to be recommended to people
> new to Clojure.
>
> Perhaps we need a way to turn off reflection warnings on
> a per-namespace basis or something? Or maybe just have 'load'
> do:
>
> (binding [*warn-on-reflection* *warn-on-reflection*] ...)
>
> ...so that it's safe to set it on and off as needed in .clj code
> files?
Other thoughts?
-Stu
Yes. This is for lib, not contrib. I think there should be a higher
bar (and I am willing to write the tests myself.)
> * Use (Klass/static) (Klass.) and (.instance obj) interop styles,
> with the only exception being in code-generating-code where the
> older (. blah) style may be easier to produce.
Good one. Added.
Stu
Stu
* Use (:property foo) to access objects, but (foo :property) to reach
into collections. The difference communicates what you think foo is.
>> I'd disagree here -- I always use (:property foo), whether foo is a
Stu
P.S. Two chousers sounds like a good idea to me too.
I know such names are idiomatic in some lisps, but haven't seen them
very often in Clojure. Other votes yay or nay?
Stu
Rewritten to:
* Unroll optional named arguments. Callers should not have to wrap
optional named arguments in a map literal:
(release-sharks 2 :laser-beams true) ; good
(release-sharks 2 {:laser-beams true}) ; bad
Oh, OK. I read 'unroll', started thinking about loops, and lost it
from there.
My opinion on this is completely tied to whatever the plan is for
supporting kw args in the language.
- Chas
nay
By far the most common Vars to be constant and not intended
to be rebound are bound to functions, and are named without
decoration. I don't see any need to for non-fns or IFns like
maps and vectors to be any different. If they're not bound
thread-locally, their definitions should be as stable and
discoverable as locals and function Vars -- if not in the direct
lexical scope, then named earlier in the file either where
they're defined or named in a :use :only list.
--Chouser
I start using somewhat redundant (-> foo :property) for some of the
code if I really want to indicate foo as an object, also prevents NPE.
DiG
I wonder if part of the disagreement depends on whether the map
key is a literal keyword or not. Rarely do I say (foo :prop)
with a literal keyword like that, but then usually if I know the
key name in the code like that it's because foo is acting as an
object not a collection.
However, (coll some-key) is not uncommon for me, and it then
often depends whether I think coll or some-key is more likely to
be nil, or whether there's any chance some-key may not be
a keyword or symbol. ...though now that I think about it
(some-key coll) seems really weird even if there's a chance coll
will be nil. I'm more likely to say (get coll some-key) which
allows nils on either.
--Chouser
Please, (swap! chouser inc) ! Chouser is not just a value, but an identity :-)
It's almost like this is something that can't easily be boiled down to
one line in a style guide ;)
All I think needs to be mandated is that code avoids NPEs in edge
cases. That's the functional requirement.
That might mean using get, or keyword-first, or an explicit guard.
Beyond that it's something that an educated programmer can decide for
themselves.
I'm wondering if this is important at the point where it is defined,
or when it is used ?
If the former, then maybe creating an alias from def to defconstant
could convey the meaning (and also open the door for defconstant
really doing something later on) ?
> - Chas
> On Feb 2, 2010, at 2:20 PM, Sean Devlin wrote:
>
> This is a bad example, it should be +speed-of-light-in-vacuum+, but
> anyway...
>
> The only use I've ever had for this is pi, and it's so central I keep it
> that short. I've never used the pluses.
>
> Sean
>
> On Tue, Feb 2, 2010 at 2:08 PM, Stuart Halloway <stuart....@gmail.com>
> wrote:
>>>
>>> And I'd add use plus signs for things that are decidedly constant and not
>>> intended to be rebound, like +speed-of-light+.
>>>
>>> Cheers,
>>>
>>> - Chas
>>
>> I know such names are idiomatic in some lisps, but haven't seen them very
>> often in Clojure. Other votes yay or nay?
>>
>> Stu
>
Well, just because *my* thinking is fuzzy doesn't mean the style
guide needs to be.
--Chouser
Definitely not. Everyone please see:
http://groups.google.com/group/clojure/browse_frm/thread/8b2c8dc96b39ddd7
for a more nuanced description of what is going on in Clojure.
> One Contradiction:
>
> * DO support operations on the wrong collection type (e.g. random access).
> Indicate any constraints in the doc, but let me worry about that as a
> consumer of the lib.
>
Also, no. In almost every case, I've regretted the few times I've let
a function apply to non-matching collection types. This is a recipe
for "Lisp is slow". People write their CL apps using assoc lists and
linear time list functions and they die when the n-squared nested perf
hits them on larger data. Functions map to abstractions and the
abstractions are supported where efficient. That is the Clojure way.
> One Personal Request:
>
> Give me enough test cases, doc strings, and a use case or two, so I can
> create an episode for each namespace. Beginner education & all that.
>
The question with this is - where does it go? Should a library jar be
bloated with example code?
Rich
The original is my recommendation, although the names in the example
are bad - these are better:
(:property object-like-map)
(collection-like-map key) ;or, use get
Rich
Please, no. Few libraries should be designed to accept nil where a map
is required, and if you are doing that, use 'get'. The maps-as-objects
vs maps-as-collections dichotomy is useful.
Rich
On Tue, Feb 2, 2010 at 5:30 AM, Stuart Halloway
<stuart....@gmail.com> wrote:
> CLOJURE.LIB CODING STANDARDS
>
> Disclaimers and Random Observations:
>
> * This is not an official or final list -- intended as provocation for
> discussion. If some of them aren't dead wrong then I didn't try hard
> enough. :-)
>
> * Rules are made to be broken, so whatever standards we come up with
> will not be absolute.
>
> * Putting my money where my mouth is: I am willing to dedicate
> significant time to editing existing code to be in line with the
> standards, once we agree on some.
>
> * This document should (eventually) be useful for programmers
> fairly new to Clojure. Links out to additional exposition is
> probably the way to go.
>
> The Standards:
>
> * Get the name and signature right. Rich strongly respects Java's
> commitment to not break existing code. In practice, that means we
> can tweak the implementation forever, but once we publish a name
> and signature we need to stick with it. (In practice I think this
> means that we want many people to review the name and sig, even if
> they don't review the implementation details.)
>
> * Use type hints where appropriate - *warn-on-reflection* should not
> bark at lib code.
>
> * Use good names, and don't be afraid to collide with names in other
> namespaces. That's what the flexible namespace support is there for.
>
> * Be explicit and minimalist about dependencies on other
> packages. (Prefer the :only option to use and require).
>
> * Don't use a macro when a function can do the job. If a macro is
> important for ease-of-use, expose the function version as
> well. (Open question: should there be a naming convention here? A
> lot of places use "foo" and "foo*".)
>
> * If you are sure you have all the information at compile time, use a
> macro where it would improve performance sensitive code. (The
> discussion on a macro for logging [2] is instructive. We end up
> needing both versions because the information is only *sometimes*
> available at compile time.)
>
> * Provide a library-level docstring.
>
> * Provide automated tests with full branch coverage.
>
> * Unroll optional arguments. That is
> (foo x y :optional z)
> not
> (foo x y {:optional z})
> --This one is from Rich.
>
> * Use '?' suffix for predicates.
>
> * Include a docstring. Except: If your function is so self-explanatory
> that the docstring cannot do anything more than repeat the name and
> arglist, then do not include a docstring. (Personally I would remove
> some of the docstrings already in place.)
>
> * When in doubt, expose the performant version. Clojure goes to great
> lengths to enable performance when you need it, and lib should
> too. (That's why we don't have multimethod + in core, for instance.)
> Users can always create more polymorphic APIs on their own,
> hijacking symbols if they want to.
>
> * If you take a good name that collides with core, make sure your
> semantics are parallel (possibly minus laziness). Good example of
> this is string functions that shadow core seq functions.
>
> * Make liberal use of assert and pre- and post- conditions. This is
> not idiomatic today, but it should be going forward. See [1].
>
> * Be lazy where possible.
>
> * Follow clojure.core's example for idiomatic names like pred and
> coll.
>
> * Do NOT follow idioms from clojure.core's preamble code. That code
> runs in a limited environment because Clojure is not bootstrapped
> yet.
>
> * Decompose the pieces. If your name isn't Rich, don't write a form as
> long as, say, the definition of doseq.
>
> * Use (:property foo) to access objects, but (foo :property) to reach
> into collections. The difference communicates what you think foo is.
>
> * Idiomatic code uses destructuring a lot. However, you should only
> destructure in the arg list if you want to communicate the
> substructure as part of the caller contract. Otherwise, destructure
> in a first-line let. Example: my snake code from the book [3] fails
> this test, doing too much destructuring in arg lists.
>
> * Prefer updating over setting. Many reasons: the unified update model
> provides a simple standard way to do this. Helps you discover
> commutative operations. Reduces the surface area of assumptions you
> are making about the object you are updating.
>
> * Don't support operations on the wrong collection type. If your
> algorithm is only performant with random access, then require an
> arg that has random access.
>
> * Use *earmuffs* only for things intended for rebinding.
>
> * Use the bang! only for things not safe in an STM transaction.
>
> * Prefer sequence-library composition over explicit loop/recur.
>
> * Rebindable vars should be paired with scoping macros, e.g. *in* and
> with-in-str.
>
> * Lazy seqs should be exposed as functions that hold only the minimum
> state needed, a.k.a. "let go of your head." Let the caller decide
> how much local memory they want to use.
>
> [1]
> http://www.assembla.com/spaces/clojure/tickets/250-turn-off-able-asserts--with-better-messages
> [2]
> http://groups.google.com/group/clojure/browse_thread/thread/79c609df3632a5d1/40fbbacf3b2cc1e5?lnk=gst&q=log4j+not+detected#40fbbacf3b2cc1e5
> [3]
> http://github.com/stuarthalloway/programming-clojure/commit/7758eac3b0434654902835fde47c854708538864
>
> --
> You received this message because you are subscribed to the Google Groups
> "Clojure Dev" group.
> To post to this group, send email to cloju...@googlegroups.com.
> To unsubscribe from this group, send email to
> clojure-dev...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/clojure-dev?hl=en.
>
>
--
And what is good, Phaedrus,
And what is not good—
Need we ask anyone to tell us these things?
Ideally, the libs should utilize the (unfinished) resource management
'scopes'. Perhaps getting those done is a prerequisite for I/O libs.
Rich
> I think promoting the use of the io! macro around stuff that cannot go
> inside a transaction would be a good idea
+1
Most of mine do so -- for example, where I'm pulling some JSON from a
remote service or a scraper, and any sequence or map could be empty or
missing. I don't see any advantage to using get without a default.
> The maps-as-objects vs maps-as-collections dichotomy is useful.
If this is currently paged into your mind, could you spare a few words
to explain why you think it's useful? (Or point me to some old post?)
get is nil-collection tolerant and supports any kind of key.
>> The maps-as-objects vs maps-as-collections dichotomy is useful.
>
> If this is currently paged into your mind, could you spare a few words to
> explain why you think it's useful? (Or point me to some old post?)
>
Well, here's the simplest separation:
It's quite rare to have something other than keywords as
field/property/attribute names,
It's quite common to have a variety of things (e.g. strings, numbers)
as keys for collection-like maps, but, of those, only keywords act as
functions. If you build library code that does (key map) it will only
work for keyword keys.
Rich
I see. Yes, that makes sense. I didn't understand your distinction,
because even my "collection-like" maps usually have keyword keys
(e.g., even scraper results typically intern the keys as keywords).
In my code, maps with non-keyword keys usually don't escape the scope
of a function. (That's not a rule, just an observation.)
Perhaps this concrete distinction could be used in a style guideline,
rather than the abstract notion "of object versus collection"? E.g.,
* If you want to have a default value if the key isn't found, use
get.
* If you know that the key you want to retrieve is a keyword, e.g.,
if the keyword is known in advance, then use (:keyword coll).
* If you're accessing keys in a heterogenous or non-keyword-key
collection, or you don't know the key in advance, and
* You know the collection cannot be nil, then
* Use (coll key)
* If the collection could be nil, either
* Use a guard, or
* Use (get coll key).
I think that concretely sums up the situations in which you might use
each approach, no?
Definitely opposed to +names-like-this+. Immutability is the default,
why have an extra convention to emphasize it?
-Phil
In my usage, this convention has nothing to do with what value is
bound to the var -- it indicates whether the var itself is intended to
be rebound.
- Chas
I don't want to promote heavy use of io! with scopes upcoming.
I could never stand the +blah+ convention so my vote is nay.
Maybe I am being dense, but if you really need a "constant"
representing the speed of light in a vacuum, then what's wrong with
(def C 2.99792458e8)? All-caps for constants has precedent.
-m
Vars that are intended to be rebound already have their own convention
too. Intended-not-to-be-rebound is implied by default.
-Phil
You got me wondering, if I did want to write a test that a name-space
contains no reflection... could I do it? Not easily at present.
I'd be happy to put together a patch to record or detect reflection
warnings in a way that can be used for testing if that's considered
useful.
> * Use type hints where appropriate - *warn-on-reflection* should not
> bark at lib code.
Sometimes reflection is inevitable or even the reason for having a
library function, as opposed to calling Java directly.
> * Use good names, and don't be afraid to collide with names in other
> namespaces. That's what the flexible namespace support is there for.
I agree in general, but what about clojure.core? Given the implied
refer-clojure in a plain (ns...), name conflicts with clojure.core are
a bit of a pain to handle.
> * Be explicit and minimalist about dependencies on other
> packages. (Prefer the :only option to use and require).
Again, should there be an exception for clojure.core?
> * When in doubt, expose the performant version. Clojure goes to great
> lengths to enable performance when you need it, and lib should
> too. (That's why we don't have multimethod + in core, for instance.)
> Users can always create more polymorphic APIs on their own,
> hijacking symbols if they want to.
Right, but that applies not just to users, also to library authors. A
library may well have the main purpose of providing generic rather
than performant functions. The recommendation should be weakened: Make
a clear distinction between performant and generic versions, and
create separate namespaces for them.
Konrad.
*var* is global-mutable
+var+ is a global-constant
Anything lexically bound (in a let) is 'normal'.
Why do I do this?
I want to differentiate between globals and lexicals.
If I'm reading code (after I have long forgotten what it was the code
was doing),
I don't want to have to look up 'is this a variable i just bound, or
is this a global-constant'.
(A lot of the time it isn't as obvious as speed-of-light-inside-a-
vacuum).
So I put +'s on them. It saves me time later.
On Feb 2, 2:08 pm, Stuart Halloway <stuart.hallo...@gmail.com> wrote:
> > And I'd add use plus signs for things that are decidedly constant
> > and not intended to be rebound, like +speed-of-light+.
>
> > Cheers,
>
> > - Chas
>
> I know such names are idiomatic in some lisps, but haven't seen them
> very often in Clojure. Other votes yay or nay?
>
> Stu
So al your functions follow the +var+ or *var* conventions ?
I guess not.
>
> If I'm reading code (after I have long forgotten what it was the code
> was doing),
> I don't want to have to look up 'is this a variable i just bound, or
> is this a global-constant'.
>
> (A lot of the time it isn't as obvious as speed-of-light-inside-a-
> vacuum).
>
> So I put +'s on them. It saves me time later.
>
> On Feb 2, 2:08 pm, Stuart Halloway <stuart.hallo...@gmail.com> wrote:
>> > And I'd add use plus signs for things that are decidedly constant
>> > and not intended to be rebound, like +speed-of-light+.
>>
>> > Cheers,
>>
>> > - Chas
>>
>> I know such names are idiomatic in some lisps, but haven't seen them
>> very often in Clojure. Other votes yay or nay?
>>
>> Stu
>
> 2010/2/4 Jonathan Smith <jonathan...@gmail.com>:
>> I use this convention all of the time.
>>
>> *var* is global-mutable
>> +var+ is a global-constant
>> Anything lexically bound (in a let) is 'normal'.
>>
>> Why do I do this?
>>
>> I want to differentiate between globals and lexicals.
>
> So al your functions follow the +var+ or *var* conventions ?
> I guess not.
I wouldn't use Jonathan's terminology, but:
(a) fn vars are essentially never meant to be rebound. In general,
designing an api so that a given fn is intended to be rebound is
probably a sign of things gone awry. So, no, the *var* and +var+
conventions are not germane w.r.t. fn vars.
(b) Some non-fn vars are obviously *intended* to be rebound by the
users of a namespace, whereas others simply hold what the namespace
considers to be +constants+, and rebinding such things will simply
break stuff. That's the intended distinction, and I think it's an
important one to make as it helps new users (e.g. so they don't have
to find the docstring that mentions "Don't rebind the *some-constant*
var, or you'll be sorry.") and future authors ("Vars *x* *y* and *z*
are often rebound, but *a* shouldn't ever change.").
Regardless, FWIW, I've long since backed away from promoting this for
the "official style guide" or whatever, as there's obviously a lot of
distaste for the practice.
Cheers,
- Chas
I have not faced the problem recently, but I guess I would do
something along those lines :
; public function, not intended to be rebound by a "client" of the
current namespace
(defn some-function [])
; private function
(defn- some-private-function [])
; public var, could be rebound by a "client" of the current namespace
(def *some-var* ... )
; private var, by definition not to be known by a "client" of the
current namespace, and thus not to be rebound by him
(def #^{:private true} *some-private-var* ...)
I also don't frequently have fns as data in the global scope, but I
guess that when I'm evolving more and more towards hof, this could
change, and then I'll also define functions that *could* be rebound by
the user, presumably seen more like functions to be passed as
arguments (function as data) than functions to be called directly by
the "client" of the namespace.
I would then follow the same convention as above, taking care of not
using defn for those functions, but rather def to promote the "this is
primarily a function-as-data" :
; public var, holding a function, could be rebound by a "client" of
the current namespace
(def *some-fn-var* (fn [] ...))
; private var, hodling a function
(def #^{:private true} *some-private-fn-var* (fn [] ...))
Following the above rules, I don't see the need to introduce the
+smth+ notation ?
>
> Regardless, FWIW, I've long since backed away from promoting this for the
> "official style guide" or whatever, as there's obviously a lot of distaste
> for the practice.
>
> Cheers,
>
> - Chas
>
> I have not faced the problem recently, but I guess I would do
> something along those lines :
>
> ; public function, not intended to be rebound by a "client" of the
> current namespace
> (defn some-function [])
>
> ; private function
> (defn- some-private-function [])
>
> ; public var, could be rebound by a "client" of the current namespace
> (def *some-var* ... )
>
> ; private var, by definition not to be known by a "client" of the
> current namespace, and thus not to be rebound by him
> (def #^{:private true} *some-private-var* ...)
Sure, marking vars that aren't part of a public API as private is
good, but that is totally orthogonal to the intention of the plus
signs, which I use to indicate rebinding intention. Some "constants"
inevitably end up needing to be public, and even those that are
private benefit from the plus signs in helping future authors know at
a glance how a var is used in the namespace.
> I also don't frequently have fns as data in the global scope, but I
> guess that when I'm evolving more and more towards hof, this could
> change, and then I'll also define functions that *could* be rebound by
> the user, presumably seen more like functions to be passed as
> arguments (function as data) than functions to be called directly by
> the "client" of the namespace.
> I would then follow the same convention as above, taking care of not
> using defn for those functions, but rather def to promote the "this is
> primarily a function-as-data" :
Yes, fns are (in my experience) almost universally created and then
passed as arguments, not bound to a var. In the one circumstance
where I did this, I believe I defined an unbound var for that purpose,
using the *earmuffs*. In any case, using ** or ++ with "normal" fn
vars seems entirely unnecessary, and I'd never suggest otherwise.
Cheers,
- Chas
You're right, I don't know why I did the merge of the 2 concepts (that
was before going to lunch, maybe this explains it ? :-) ).
But then, again (and again :-), if you have only 2 "kinds" of globals,
those that are not intended to be rebound, and the others, then
certainly having 2 different ways to express them is sufficient:
*could-be-rebound*
not-intended-to-be-rebound
So I guess that by introducing +foo+, you also convey the information
that +foo+ is defined as a root var.
So a symbol can designate:
1. a global/local var
a. either intended to be rebound in dynamic scope
b. either not intended to be rebound in dynamic scope
c. can be shadowed in lexical scope
2. a formal argument of a function or macro definition
a. cannot be rebound by design, just shadowed in lexical scope
3. bound by a let
a. cannot be rebound by design, just shadowed in lexical scope
Do you feel the need to adopt a convention to distinguish symbols
representing variables in #{1. 2. 3.} ? Or just #{1. #{2. 3.}} ?
Did you never encounter the case where you use (intentionally or
rather not intentionally) the symbol of a core function as the name of
a variable in a let form ?
e.g. (let [str "foo"] ... ... ... (str "{" str <- woops :-)
> , which I use
> to indicate rebinding intention. Some "constants" inevitably end up needing
> to be public, and even those that are private benefit from the plus signs in
> helping future authors know at a glance how a var is used in the namespace.
>
>> I also don't frequently have fns as data in the global scope, but I
>> guess that when I'm evolving more and more towards hof, this could
>> change, and then I'll also define functions that *could* be rebound by
>> the user, presumably seen more like functions to be passed as
>> arguments (function as data) than functions to be called directly by
>> the "client" of the namespace.
>> I would then follow the same convention as above, taking care of not
>> using defn for those functions, but rather def to promote the "this is
>> primarily a function-as-data" :
>
> Yes, fns are (in my experience) almost universally created and then passed
> as arguments, not bound to a var. In the one circumstance where I did this,
> I believe I defined an unbound var for that purpose, using the *earmuffs*.
> In any case, using ** or ++ with "normal" fn vars seems entirely
> unnecessary, and I'd never suggest otherwise.
>
> Cheers,
>
> - Chas
>
If I were to do a function that I intended to have rebound, I probably
would put earmuffs on it.
You do point out another obvious advantage,
as function vars become syntactically differentiated from variables.
I don't really think any of this is a big deal,
I just think it should be clear that there are people who do this sort
of thing and think it has advantages.
On Feb 4, 3:10 am, Laurent PETIT <laurent.pe...@gmail.com> wrote:
> 2010/2/4 Jonathan Smith <jonathansmith...@gmail.com>:
I'd wonder further what public constants *are* exposed by the libs.
Are there any? What are they?
Rich
No, there are none. My bringing up +names-like-this+ was totally
outside the context of clojure.lib -- at some point, things veered off
into discussing conventions in general.
- Chas
> --
> You received this message because you are subscribed to the Google Groups "Clojure Dev" group.
> To post to this group, send email to cloju...@googlegroups.com.
> To unsubscribe from this group, send email to clojure-dev...@googlegroups.com.
I thought it is a pain, but in practice that is why refer-clojure is there and I used it couple of time for things like sftp/get which goes together with sftp/put, etc. and it makes a lot of sense too me.
>>
>>> * Be explicit and minimalist about dependencies on other
>>> packages. (Prefer the :only option to use and require).
>>
>> Again, should there be an exception for clojure.core?
I usually do not use refer-clojure unless I am overriding core symbol in my lib code. I did migrate to using :require instead of :use almost exclusively everywhere. In the project with more than one developer it helps a lot to look at the header and no exactly what is used. And more importantly be able to search for duck-stream/reader or ds/reader in the file and not just reader which could be a variable, etc.
Regards,
DiG