The usual approach to this kind of things is to have a Functional
representation of the world, that contains the game logic. To have
pure functions that modifies the world with the time and inputs of
players.
And everytime you want to render a frame to call a render function
that does all the
In a few lines:
Monads are a common framework to represent any sequential computation.
What is a sequential computation?
- either no computation at all.
return :: a - m a
does that.
- or I have already a computation and want to go on with my computation.
But then, I need to be able to look
I would think so.
But to maximise your chances of getting useful help, it is often
better to post small pieces of codes and explain what
you are trying to achieve with them.
Best,
Nicolas.
--
You received this message because you are subscribed to the Google
Groups Clojure group.
To post to
You have 2 distinct problems:
- allocating many short-lived objects. It can affect performance but
does not matter for long-term memory occupation
- keeping too many things in memory. Then you have to resolve an
algorithmic question, quite independent of
the technology you use.
On Wed, Oct 3,
Depending of the code they have to write, you can walk their code at
compile time and transform it to
monadic normal form or CPS style, which would allow to do what you want.
I would tend to think that with a lot of threads you will win with
closures and not native threads.
And would only a few
Clojure is a good language to start.
The only thing is that there are more ressource for beginners in other
languages.
For Racket (another Lisp), you have the very good:
How to design programs.
http://www.ccs.neu.edu/home/matthias/HtDP2e/
(this is the second edition, you might have to peak in
No continuations or coroutines support on the JVM, as far as I know.
However, there are a few monad libraries for clojure with which you
would probably
be able to do what you want. (Using the continuation monad...)
http://www.intensivesystems.net/tutorials/monads_101.html , for example.
I tend to like 1 better. I do not like working with vars without a good reason.
--
You received this message because you are subscribed to the Google
Groups Clojure group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be
It is quite easy to write a macro define in clojure that does that:
(defmacro define [head body]
(if (sequential? head)
`(define ~(first head) (fn [ ~@(rest head)] ~@body))
`(def ~head ~@body)))
(define ((A [p1 p2]) [p3 p4]) [p2 p4]))
((A [1 2]) [3 4]) = [2 4]
--
You
On Sun, Aug 26, 2012 at 3:07 PM, David Nolen dnolen.li...@gmail.com wrote:
I haven't found this to be the case. Java fares pretty well on Alioth.
http://shootout.alioth.debian.org/help.php#java
Shows that it does not change much on programs that run for mor than a
few seconds.
--
You received
1. Gary Bernhardt has been playing with a new approach he calls
Functional Core, Imperative Shell. Essentially, it's another take on
the question of how to limit the scope of mutation in order to get the
most out of the correctness of mutation-free algorithms and the
performance of mutating
Here's my take:
We want to define a function my-comp. It takes n functions and return
their composition.
We want to return a function of any number of arguments, so let's
start by working with a given
set of argument args, and returning the value of the composition
applied to those arguments.
-
More optimsation ideas:
(defn next-level [b dir]
(r/map #(Move-Board. % (core/try-move %))
(core/team-moves @curr-game b dir))) ;;curr-game is a promise
(definline team-moves
[game b dir]
`(let [team# (gather-team ~b ~dir)
tmvs# (r/mapcat (fn [p#] (r/map
Dear all,
After reading this:
http://infoscience.epfl.ch/record/169879/files/RMTrees.pdf
I was wondering what was the current performance characteristics of
the different ways to
concatenate arrays in clojure?
Best regards,
Nicolas.
--
You received this message because you are subscribed to
(and creates vectors via (into [] (r/map )))
Depending of your method of scoring, you could try to do it just with a reducer.
(Without creating a vector with it).
If your code does not spend its time in GC (can be seen in the first
pane), CPU sampling might be a better place to look.
--
(defn score-by-count ^long [b dir]
(let [ hm (into [] (core/gather-team b dir))
aw (into [] (core/gather-team b (unchecked-negate dir)))]
(unchecked-subtract (count hm)
(count aw
(defn counting-accumulator [acc _]
(inc acc))
(defn score-by-count ^long [b
If it is so slow, that's maybe because the branching factor is very high.
Could you have an atom incremented in your evaluation function, or in
next level, to check how many boards are generated for level 2 or 3?
(At least this could give an approximate time for computing each board.
(83 - 8) /
: Anyone with a free account can add/edit examples on that site.
Andy
On Aug 21, 2012, at 8:34 AM, nicolas.o...@gmail.com wrote:
I understand now.
The documentation could be clearer on that.
Your triangular example is very clear.
--
You received this message because you are subscribed
Hi,
3 questions:
1. Are you sure your moves list at the op level is in a vector?
Looking at the code for reducers, it seems that it is the only
implementation actually doing concurrency.
2. The default block size for spawning a new thread is 512. Meaning
that if you have
less than 512 first
Sorry, I forgot to convert to a vector:
(defn best-move Start folding here. [dir b d]
(r/fold 1 best best
(r/map #(Move-Value. (:move %) (search score-by-count (:tree %) d))
(into [] (:children (game-tree
dir b next-level))
--
You received this
In particular, if I attempt to replace the `r/reduce` call on line #23,
with a call to `r/fold`, I get the following crash:
This calss sems strange.
remaining? should represent a monoid for it to work.
Meaning two functions:
1 - A and (A - A - A)
In your code, the case with no arg returns a
On Wed, Aug 22, 2012 at 1:53 PM, Jim - FooBar(); jimpil1...@gmail.com wrote:
Ok, so I followed your suggestions and the block size did the trick with
regards to using all 4 cores of mine...However, even so, I get no
performance improvements!!! It still needs close to 4 min to go to level 4
and
I'm really sorry but I don't follow...I'm only doing (:value best) or
(:value next). best or next return a Move-Value (it has :move and :value
keys) where the :value key could be Integer/MIN_VALUE - I'm not doing
(:value Integer/MIN_VALUE) anywhere...
Then you want to write:
(defn best
([]
You should replace your functions that computes the board by function
that does return 30 times the same board.
And evaluation function by something that returns a constant value.
And check : speed and speed-up for folding.
Then you will know for sure whether the slowness comes from the
explore
=
On Tue, Aug 21, 2012 at 11:50 AM, Arie van Wingerden xapw...@gmail.com wrote:
Extra restrictions on (range of values of) variables used in the for.
See here:
http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/for
The link says nothing about the meaning of the modifiers.
(I
Dear all,
What is the meaning of :while in a for?
I understand :when, and also that :while jumps more element when the
condition is not met,
but where does it jump to exactly?
Best regards,
Nicolas.
--
You received this message because you are subscribed to the Google
Groups Clojure group.
To
I understand now.
The documentation could be clearer on that.
Your triangular example is very clear.
--
You received this message because you are subscribed to the Google
Groups Clojure group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are
(defn generate [board next-boards]
;; next-boards return a seq of MoveAndBoard
(BoardAndChildren. board
(r/map (fn [m]
(MoveAndTree.
(:move m)
(generate (:board m)
How do I construct a combining fn out of 'max-key' and is there an idiom or
a pattern for doing so?
You might use the fact that -Infinity is a neutral element for max.
(Or the smallest long if you work with longs).
Alternatively you can represent your value as either a number or nil,
nil being
Hi Jim,
I tried a bit.
The performance of this is not perfect but seems not horrible.
It can do a level 5 exploration in 11 sec on my computer, with a
branching factor of 30.
(Of course, there is very little computation for the next move.)
The generation is now lazy, but the depth is used in the
By the way, I just found an obvious bug in that code, but
that should be easy to correct.
(if (= res Double/NEGATIVE_INFINITY) (evaluate (:board tree))
res
This is obviously wrong.
--
You received this message because you are subscribed to the Google
Groups Clojure group.
To
thanks a lot Nicolas...I'll definitely play around with your code tomorrow
morning...if you say you can go up to level 5 in 11 sec this is really good
performance -I can't wait to explore the code...I'll let you know of any
comments of mine soon!
Of course, don't trust this code. I have
A correction for the wrong part:
(defn my-max ([x y] (if (nil? x) y
(if (nil? y) x
(max x y
([] nil))
(defn tree-value ^double [tree evaluate ^long depth]
(let [children (:children tree)]
(if (or (zero? depth))
(evaluate (:board
First solution:
(defn groupSum [l x]
(or (zero? x) ; we have won!
(and (not ( x 0)) ; we can't win
(not (empty? l)) ; lost!
(or (groupSum (rest l) (- x (first l)))
(recur (rest l) x)
The we
Visualvm is quite nice and simple to use.
On 10 Aug 2012 23:21, Hussein B. hubaghd...@gmail.com wrote:
Hi,
I want to measure how much space an algorithm is taking and then trying to
change some aspects to see how things are going to differ.
I also want to measure how much time it takes to
There is no such thing in Clojure.
Separation of IO and non IO is done by:
- encouraging pure functions
- providing very good pure data structures, and libraries
--
You received this message because you are subscribed to the Google
Groups Clojure group.
To post to this group, send email to
trampolines is a slightly different example.
--
You received this message because you are subscribed to the Google
Groups Clojure group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your
first post.
To
(defrecord MyRecord [x y z]
(make-record [arg1 arg2 arg3 ...] ...))
(make-record MyRecord arg1 arg2 arg3 ...)
This construct is not very good. make-record is not first-class (It
cannot be used as an argument to a function).
Its first argument is not first-class (it has to be statically the
(defn recurse [f] (fn [ args] (apply f (recurse f) args)))
(def fac
(recurse (fn [self n]
(if (zero? n)
1
(* n (self (dec n)))
There is a performance cost. recursive can be specialised on different
number of arguments, to reduce
this cost.
My answer to this is: everything is an implementation at some level. So do
you think we need factory functions of factory functions, and factory
functions of factory functions of factory functions? I am sure that will be
more flexible than just one level of factory functions.
We already have
Constructors are not a good idea, because they tie the functionality
to the implementation.
You want to be able to change your record to something else without
changing your client code.
--
You received this message because you are subscribed to the Google
Groups Clojure group.
To post to this
The technique, using throw an Exception when succeeded in searching, strikes
me!
You can make it less ugly with a bit of library:
user (deftype EarlyExit [result])
user.EarlyExit
user (defn early-exit [x] (EarlyExit. x))
user (defn reduce-with-early-exit [f acc coll]
(if (instance?
Another one. (The exception is for early termination)
(def found! (Exception.))
(defn has22 [l]
(try
(reduce #(and (= 2 %2) (or (not %1) (throw found!))) false l)
false
(catch Exception e true)))
--
You received this message because you are subscribed
There is indeed an infinite number of functions, or relationships between
natural numbers. I don't think that means that that any one of those
relationships is not computable because it is within the range of infinite
functions. The countable parts of a program can still accept an infinite
Hi,
Great notes. I like a lot.
A few (mostly technical) comments:
Concept of Computation
What does it mean to compute? Turns out this is a complicated question. From
what I can tell, to compute is an actualization (or concrete version) of a
mathematical function. We are swimming in a
On Mon, Jul 23, 2012 at 3:14 PM, Mimmo Cosenza mimmo.cose...@gmail.com wrote:
hi Merek and thanks for the link. But it does not answer my question.
I was looking for a demonstration of the reducibility of (not tail)
recursion to tail recursion. Or there is a demonstration of that, or nobody
Yes.
For example, you can have a look at CPS transformation.
If you want tail recursion and not tail calls, you can add
trampolining to the mix.
--
You received this message because you are subscribed to the Google
Groups Clojure group.
To post to this group, send email to
(defn move
The function responsible for moving Pieces. Each piece knows how to move
itself. If trying? is true, there will be no histiry of the new state of the
board. Returns the new board.
^clojure.lang.PersistentVector
[game mappings p coords]
{:pre [(satisfies? Piece p)]} ;safety
(defmacro in?
Returns true if colle contains elm, false otherwise.
[colle elm]
`(if (some #{~elm} ~colle) true false))
Yes. Should not be a macro. (There is no reason for it to be a macro).
On top of that, it is not very often useful to convert nil to false as
clojure understands
On Thu, Jun 14, 2012 at 6:19 PM, Jim - FooBar(); jimpil1...@gmail.comwrote:
I don't see why not if you really really need to return true/false...
Because it can be written as a function. Macro are only for things that
cannot be easily written as functions.
(And then it should be backed by
why add the extra overhead of potentially boxing/unboxing x in such a
simple case? Its not like the macro is getting out of control...Its a one
liner...
Because functions are first class and not macros.
Ex:
(map to-bool l)
--
You received this message because you are subscribed to the
that mutation inside piece is
the only non-functional detail...everything else I am indeed returning new
boards, new pieces (promotion, death etc)...
If I were you, I would look into a way to make Pieces pure too.
Because their mutability cascades into the whole state and makes the
On Wed, Jun 13, 2012 at 10:46 AM, nicolas.o...@gmail.com
nicolas.o...@gmail.com wrote:
that mutation inside piece is
the only non-functional detail...everything else I am indeed returning new
boards, new pieces (promotion, death etc)...
And from what I understand of your design, I disagree
different positions now or some are nil (dead))... if i make the pieces pure
what comes to mind is resetting or swapping possibly 32 atoms instead..
No, you will have an atom containing a new board with new pieces in
their new position.
(By the way, you can go a long way writing what you want
you mean making 32 new pieces after each move regardless of whether they
moved or not? how can I identify the ones that are different from the ones
that remain unchanged so i can conj them in a new list? move will eventually
call 'build-board' which has to look somewhere for the current
Stack-overflows in quicksort can mean that you are hitting the worst
case of this algorithm complexity.
(Meaning that you are sorting an array already sorted in one direction
or the other.
In this situation, the size of the recursive call are n - 1 and 0,
instead of being roughly n/2 for both
It is not totally clear in your post how you want to keep the data?
Is it in memory (with a transactional log somewhere)?
If it is the case, you can do better than reducing the whole data set
when executing a query:
you can keep a cache of query results, or indexed data and maintain
it, while
I agree with the other commenters that letfn is good when you can use
it, but here you can't use it. Your general approach is really the
best you can do, though it's nicer to use promise/deliver than atom/
swap!, since these values are never going to change again.
I was looking for something
Dear all,
Is there a way to write something like:
(let [x (foo1 (fn [] (bar y)))
y (foo2 x)] )
where the y on line 1 refers to y in line 2?
I currently use an atom and an affectation to tie the loop...
Best,
Nicolas.
--
You received this message because you are subscribed to
Everyone just reiterates the mantra “It's slower! It's slower! It's
slower!”, but no one talks about the trade-offs. And I bet only a very
small fraction ever checked what “slower” means in their specific use
case.
I think the whole thread is about the trade-off: some people complains
that
The only exception is mutable fields in the type where you don't want
uncontrolled access. There you indeed need a protocol to handle the
synchronisation of the field access.
And then you need to include everything accessing the internal state of
your data structures in a big deftype with
Access internal state?
Like __hash in your code.
For example, the earlier hash example cannot be put as an extension.
Why should most extensions manipulate mutable fields?
Most certainly won't. Now none of them can.
--
You received this message because you are subscribed to the Google
Have you actually looked at the data structure implementations in
ClojureScript? The work is done. It's all protocols.
Hi David,
I just have. This is a nice work. There is a lot of repetitions though.
For example:
- IEquiv
(-equiv [coll other] (equiv-sequential coll other))
- IHash
And now any of those implementations can easily change at anytime without
any external considerations. So what's the problem?
Well if you want to amend all of them at the same time, you have to
modify all of them.
On your advice of using extend-type: maybe I am wrong but I think it
has a
Same here--I can count on one hand the number of times I've wanted to
implement polymorphism in three and a half years. Every time
multimethods have worked great for the task. If you had polymorphism
in a tight loop they might not suffice, but the decision to abandon
multimethods should only
On the other hand, defrecord/deftype encourage you to write your protocol
implementation in a way that cannot be reused unless you very specifically
plan for it (factoring the protocol implementation into a separate map) and
use a coding style that (might?) be less efficient (i.e., adding the
(extend Employee
AProtocol
(merge default-implementation {:new-function (fn ...)}))
But you lose a bit of performance with that...
--
You received this message because you are subscribed to the Google
Groups Clojure group.
To post to this group, send email to
I had wished such a feature all week.
You don't need it often, but when you need it, it is really annoying
to circumvent.
traits would really be useful.
--
You received this message because you are subscribed to the Google
Groups Clojure group.
To post to this group, send email to
So I suggest you take it to heart. Put deftype, defrecord and defprotocol
away and don't pull them back out for quite some time. At the beginning,
they are just a distraction from Clojure's core philosophy. Focus on maps,
vectors, sets, functions, multimethods and macros.
But there are
LoL is a good book. Very entertaining, even if part of the content is debatable.
(It is a very opiniated book)
Reading it after On Lisp could be a good idea, as there are a few
references from LoL to On Lisp.
http://www.paulgraham.com/onlisp.html
The book is definitely a good read, makes you
I think part of the problem is that commute should not return any result.
You are not sure this is actually the value that will be returned,
this is costly
and misleading.
There should be at least a variant of commute that return nil.
--
You received this message because you are subscribed to
Any android port in the pipeline?
--
You received this message because you are subscribed to the Google
Groups Clojure group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe
Dear all,
Is there a way to apply a function to all members of a transient map?
Best regards,
Nicolas.
--
You received this message because you are subscribed to the Google
Groups Clojure group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members
So, if I have a transient and want to reduce it and then continue to
use it transiently,
I need to call persistent!, reduce and transient again.
Is there a reason for that?
(The documentation says that read operation are allowed. And it is
actually possible
to write a reduce for a transient
On Fri, May 11, 2012 at 12:47 AM, Rich Hickey richhic...@gmail.com wrote:
IMO, Nicolas' material is a distraction in understanding reducers, except as
historical background.
I perfectly agree.
--
You received this message because you are subscribed to the Google
Groups Clojure group.
To post
I can describe the background to understand my last email.
From the programming point of view, I have been told since yesterday
that what I explained has already been explained better in Stream
Fusion From Lists to Streams to Nothing at All from Coutts
Leschinskiy and Stewart:
It looks very good.
Just a few side-notes:
- this representation is quite well known, as it is the Church
encoding of list, often used in System F, for example.
It corresponds to stating the definition of sequences as the
initial algebra of F A = 1 + Any x A.
- you can play a dual trick for
On Wed, May 9, 2012 at 1:00 PM, Rich Hickey richhic...@gmail.com wrote:
This analogy is not quite right.
(fn [n] (vector n (+ n 1))
is not a reducing fn. Tt is a cons cell builder, and the Church encoding
builds lists.
It is because I did not write it in the right way. Sorry about that.
It
On Wed, May 9, 2012 at 5:01 PM, Rostislav Svoboda
rostislav.svob...@gmail.com wrote:
On 9 May 2012 17:31, Tassilo Horn tass...@member.fsf.org wrote:
you should bind *read-eval* to false when reading data from unknown sources.
This is the point! On one hand I need to evaluate data from a client
Part of the problem is that you confuse compile time and runtime.
Macro are evaluated at compile-time.
So when you write:
(my-macro SomeClass. a b) , my-macro can't access the runtime value of a and b.
So if you want your second example to work, you need flatten to be
called at runtime and not
On Fri, Apr 20, 2012 at 11:14 AM, Marc Limotte mslimo...@gmail.com wrote:
Thomas,
Try this:
(defmacro my-macro2 [func args] `(~func ~@(flatten (eval (vec args)
This won't work for:
(let [a [2 3]] (my-macro2 SomeClass. a))
--
You received this message because you are subscribed to
This is not the idiomatic way but you can stay quite close from your code by:
(defn game [ board ]
(fn [n i]
(cond
(= n :x) (game (assoc board i 'x))
( = n :o) (game (assoc board i 'o))
(= n :print) (println board
(defn (new-game (game
Did you have a look at:
http://www.cs.cmu.edu/~rwh/theses/okasaki.pdf
After p. 50 there is a description of dequeues. I don't know how they
compare to yours.
--
You received this message because you are subscribed to the Google
Groups Clojure group.
To post to this group, send email to
Is this also useful for implementing something like Datalog? If yes,
in what ways?
Regards,
Shantanu
I don't know for Datalog but for Prolog.
Imagine you have
parent(a,b).
parent(b,c).
ancestor(X,Y) :- parent(X,Z) , ancestor(Z,Y). (1)
ancestor(X,Y) :- parent(X,Y). (2)
Now you have the
On Mon, Jan 17, 2011 at 3:10 AM, chris cnuern...@gmail.com wrote:
Is it insane to suggest that perhaps clojure should work with scala
such that we can write both languages in the same file?
A lot of reasons for which it is not possible:
- it would mean coordinating two
I never was fully convinced an atom around a functional hash was
perfect for concurrency.
There is no write/write or read/write concurrency possible, even on
independent data.
Someone was working a while ago ob TransactionalHashMap, if I recall well.
Is there something already to benchmark
After a few thoughts, I think this is a mistake not to allow this,
even if it his highly discouraged.
I think indeed, if you consider the data-structure usage of types and
Objects it is a very bad idea to have a mutable private field.
But some type you create are not for holding data on the
Dear all,
Is there a way to make some mutable fields public in a deftype?
Best regards,
Nicolas
--
You received this message because you are subscribed to the Google
Groups Clojure group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are
On Thu, Dec 23, 2010 at 9:28 PM, Meikel Brandmeyer m...@kotka.de wrote:
Hi,
Am 23.12.2010 um 15:26 schrieb nicolas.o...@gmail.com:
Is there a way to make some mutable fields public in a deftype?
I think it is opinionated, that this is not possible as the documentation
states explicitly
On Thu, Dec 23, 2010 at 10:29 PM, Meikel Brandmeyer m...@kotka.de wrote:
Hi,
Am 23.12.2010 um 23:15 schrieb nicolas.o...@gmail.com:
If I were to write such a patch, would it be accepted?
I lean now quite a bit out the window, but I dare say: „No.“ The reason I
think so
Which version of Clojure are you using?
(How to speed that up depends a lot of the version you use)
I would like to see a with a longer run.
Optimised clojure is *asymptotically* nearly as fast as Java.
With under 1000 calls I am not sure the JIT is called.
Best,
Nicolas.
On Wed, Dec 22, 2010
If you want native with enough reflection to compile clojure,
Objective-C might be a better choice than C++.
--
You received this message because you are subscribed to the Google
Groups Clojure group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members
I am not a Clojure expert. But if I understood Clojure correctly,
Clojure would not be Clojure if it where natively compiled. Eg. The
whole lazy seq's are required because of lack of tail call
optimization in the JVM. Or am I wrong?
I don't think the lazy seq are necessary because of TCO.
In my experience lazy-seqs are a reasonable replacement for the lack of TCO,
and from what I've heard that is one of the reasons they exist.
(defn a [x]
(lazy-seq
(cons x (b (inc x)
(defn b [x]
(lazy-seq
(cons x (a (inc x)
David
I am not sure I get you. COuld you
Those are mutual recursive functions. Trying to define them as regular
functions will quickly result in a stack overflow.
You could use trampoline but in my experience you will take a significant
performance hit.
Lazy sequences are a way to efficiently represent mutually recursive
With TCO mutually recursive functions do not consume the stack. The same is
true for well constructed lazy sequences.
David
With TCO, mutually *tail* recursive functions do not consume the stack.
non-tail call always consume the stack in non CPS compiled languages.
(In which, it consumes the
Yes I know, I thought the way I wrote the code was clear about that :)
I am sorry, I did not understand.
So let me try to explicit your transformation (please correct me if I am wrong):
- start with some mutually recursive functions: a and b, for example
- create a lazy stack for the recursive
I have an example to clarify what I understood of your idea:
(declare odd)
(defn even [x]
(if (zero? x)
[true]
(lazy-seq nil (odd (dec x)
(defn odd [ x]
(if (zero? x)
[false]
(lazy-seq nil (even (dec x)
(defn get-value [l]
It's a funy and original trick but on this example at least,
it seems slower (and it can use a lot of memory, because it retains a
stack in the heap) than trampoline:
(time (get-value (even 1500)))
Elapsed time: 1899.881769 msecs
And a version with trampoline
(defn odd [^long x]
(if
If you wait for clojure in clojure and then use VMkit (LLVM based
thing to do Virtual Machine), it can be an interesting project.
I am not sure if it would be considered as really native, though.
Nicolas.
--
You received this message because you are subscribed to the Google
Groups Clojure
1 - 100 of 139 matches
Mail list logo