#clojure log - Sep 19 2009

The Joy of Clojure
Main Clojure site
Google Group
IRC
List of all logged dates

1:13 talios: Afternoon

1:31 JAS415: what is the difference between an atom and a ref?

1:32 hiredman: refs are coordinated

1:32 JAS415: is it just the transactional locking?

1:32 hiredman: pretty much

1:34 JAS415: so i would use a ref if i were for example, doing an in memory database that is getting updated from multiple threads

1:34 and i wanted it to be safe

1:35 hiredman: you would use a ref if you needed to coordinate mutation

1:36 you can update an atom from multiple threads no problem

1:36 clojurebot: People have a problem and think "Hey! I'll use a regular expression!". Now they have two problems....

1:38 hiredman: http://groups.google.com/group/clojure/msg/fd0371eb7238e933 <-- rhickey on atoms/refs/agents

1:38 JAS415: so i guess i'm just mulling over what it is to coordinate mutation

1:39 hiredman: if the new value of Ref1 is a function of the value of Ref2

1:40 JAS415: oh okay

1:40 so its a mechanism to keep them in sync

1:40 hiredman:

1:41 coordinate

1:41 ugh

1:41 JAS415: coordinate

1:41 sorry

1:42 hiredman: sorry, ugh was for something else

1:42 JAS415: I'll try to read this stuff and understand it

1:43 the impression that i'm getting is that atom will be enough for what i'm doing currently

3:56 leafw: Houston, I have a problem. I can't start the clojure Repl from the github master branch

3:56 I get: java.lang.ExceptionInInitializerErr

3:57 at clojure.main.<clinit>(main.java:20)

3:57 I am trying to launch it like: java -cp jars/clojure.jar clojure.main

3:57 clojure.lang.Repl also fails.

3:57 the line that fails in clojure.main is final static private Var REQUIRE = RT.var("clojure.core", "require");

3:59 I built clojure the old way ... so now, only when doing so with ant and AOT generation of class files, clojure will work?

3:59 talios: does the same happen running 'java -jar clojure.jar'?

4:00 hiredman: leafw: runs fine here, try ant clean and rebuild

4:00 what do you mean the old way?

4:00 leafw: talios: yes.

4:00 hiredman: only .clj files inside; no .class files.

4:01 clojure.jar with .class files inside runs just fine.

4:01 hiredman: a. that is not going to work, the jvm only executes bytecode, so there need to be some .class files in there

4:01 talios: given that a lot of clojure is written in java - you'd need -some- .class files...

4:02 leafw: hiredman: ok. I get it.

4:02 I just haven't used the ant way of building in ages.

4:02 talios: theres another way?

4:02 hiredman: b. the slim jar files generate by ant compile the java code and leave the clojure code as .clj files

4:03 leafw: then how are you building if not with ant?

4:03 * talios runs off to grab his dinner

4:03 leafw: hiredman: with our own build system. I found out about the differences when doing an update.

4:04 hiredman: :(

4:04 I would recommend using clojure's ant setup

4:04 it's there, and it gets used

4:05 leafw: hiredman: sure, but it's just inconvenient when building clojure as part of another ~100 jars or so. Ant xml is just ... not expressive enough.

4:05 hiredman: using a private building system for it is bound to run into problems as clojure changes over time

4:06 leafw: hiredman: well, as maintainer, I will be fixing it.

4:07 talios: maintainer of what?

4:07 * talios just uses the maven snapshots from the formos repo

4:08 leafw: clojure in fiji : http://pacific.mpi-cbg.de

4:09 talios: oh - I thought it was a clojure deployment IN Fiji :)

4:09 hiredman: if clojure's current build system is not enough, it's better if it's made to be enough, and not have people maintaining a bunch of code in private

4:10 (not that it has much of a build system)

4:10 leafw: clojure AOT is one of a kind.

4:10 nver mind, we are happy.

4:11 talios: mmm that reminds me - must check my github forkqueue for the maven plugin

4:11 hiredman: I'm just saying, it makes everyone's life easier if code is pushed out

4:17 talios: bah - a) can't apply stuart sierra's patched as they break things, b) github just went down, c) out of coke :(

4:17 s/ed/es/

5:07 tomoj: hmm.. I don't think *print-level* should affect eldoc

6:39 pixelman: Writing a nested if seems like it could be done with a macro instead, what I have is "if a elsif b elsif c else d". where should I look?

6:39 arbscht: ,(doc cond)

6:39 clojurebot: "([& clauses]); Takes a set of test/expr pairs. It evaluates each test one at a time. If a test returns logical true, cond evaluates and returns the value of the corresponding expr and doesn't evaluate any of the other tests or exprs. (cond) returns nil."

6:40 pixelman: sweet! tnx.

6:41 arbscht: ,(doc case)

6:41 clojurebot: "clojure.contrib.fcase/case;[[test-value & clauses]]; Like cond, but test-value is compared against the value of each test expression with =. If they are equal, executes the \"body\" expression. Optional last expression is executed if none of the test expressions match."

6:56 triyo: Can someone explain to me how the clojure.core/future works? Maybe through some example or something pls.

6:58 ,(doc future)

6:58 clojurebot: "([& body]); Takes a body of expressions and yields a future object that will invoke the body in another thread, and will cache the result and return it on all subsequent calls to deref/@. If the computation has not yet finished, calls to deref/@ will block."

6:58 hiredman: it wraps code in a thunk, and runs on the thunk on another thread

6:59 futures can be derefed

6:59 if you deref them, they block waiting for the result of the thunk

6:59 triyo: hiredman: what does thunk mean?

6:59 hiredman: (fn [] )

7:00 triyo: oh ok

7:01 so future will actually be ran on a saperate thread correct?

7:01 hiredman: futures are run on the agent send-off threadpool

7:02 triyo: ahh, thanks, now that makes sense, i understand

7:04 hiredman: I was going through this updated example of "sleeping barber" http://gist.github.com/189002 and came across future func and wasn't to sure how it works

7:45 killy971: in clojure, map is lazy by default ?

7:47 tomoj: killy971: map returns a lazy sequence, yes

7:48 killy971: is it possible to use a non-lazy version of it ? (for performance)

7:48 tomoj: not that I know of

7:50 you could write your own I suppose

7:51 is the laziness of map really a bottleneck?

7:52 killy971: I suppose that it is not actually...

7:53 arbscht: ,(doc doall)

7:53 clojurebot: "([coll] [n coll]); When lazy sequences are produced via functions that have side effects, any effects other than those needed to produce the first element in the seq do not occur until the seq is consumed. doall can be used to force any effects. Walks through the successive nexts of the seq, retains the head and returns it, thus causing the entire seq to reside in memory at one time."

7:53 killy971: but doall it just there to force the evaluation by the way

7:54 it won't get rid of the internal lazy processing

7:54 arbscht: oh right, performance

7:54 killy971: but actually I shouldn't think of this as a bottleneck

7:55 arbscht: yeah, I don't see the problem with it

8:00 killy971: I was trying to make a fast prime sieve

8:01 the clojure version of the first answer on this page : http://stackoverflow.com/questions/1023768/sieve-of-atkin-explanation

8:02 when I read the wiki about the sieve of atkin, I didn't see the relation between this implementation and the original algorithm... but if I just believe the SO page, it would mean that this algorithm if fundamentally faster than the eratosthene sieve

8:03 so I implemented it in clojure, and compared performance with the eratosthene one, and I can't get as fast as it....

8:05 my reference for the eratosthene sieve implementation : http://paste.lisp.org/display/69952

8:07 something that I especially don't understand here is the fast that (aset arr j (int 1)) is far faster than (aset-int arr j 1)

8:07 but in my implementation, it makes no difference

8:11 hiredman: sure

8:12 aset is fastern than any of the aset-* functions

8:13 tomoj: why do the aset-* functions exist?

8:13 hiredman: I forget

8:13 tomoj: maybe aset used to not work with primitives?

8:13 pixelman: how can i pretty print any object i clojure, I'm looking for something like ruby's .inspect

8:14 tomoj: pixelman: there's clojure.contrib.pprint

8:15 pixelman: tomoj: thanks!

8:17 killy971: aset is fastern than any of the aset-* functions >> but replacing (aset-int arr i 1) by (aset arr i (int 1)) in my code doesn't change anything... so I was wondering why did it change performance on rich hickey code

8:20 hiredman: ~performance

8:20 clojurebot: http://clojure.org/java_interop#toc46

8:24 killy971: in my code, I have an (aset arr i 1) producing in a reflection warning

8:24 so I suppose using aset-int is the only way to solve this ?

8:25 hiredman: no

8:25 you need to hint arr and i

8:25 or coerce

8:28 leafw: hum

8:28 ,(unchecked-add (byte 4) (byte 7))

8:28 clojurebot: java.lang.IllegalArgumentException: No matching method found: unchecked_add

8:28 leafw: ,(find-doc "unchecked-add")

8:28 clojurebot: ------------------------- clojure.core/unchecked-add ([x y]) Returns the sum of x and y, both int or long. Note - uses a primitive operator subject to overflow.

8:28 leafw: ???

8:28 what am I doing wrong?

8:29 killy971: is it possible to have a type hint a let for an int-array ?

8:29 leafw: killy971: yes: (let [#ints ia (make-array Integer/TYPE 10)] a)

8:30 killy971: I have 2 loops

8:30 leafw: ,(unchecked-add 4 7)

8:30 clojurebot: 11

8:30 leafw: ha! So unchecked-add doesn't like bytes

8:30 killy971: in the fouter one I have an int-array, that I give to the inner loop

8:31 leafw: killy971: if you are looping, you are likely doing something wrong.

8:31 use amap or areduce on primitive arrays.

8:31 killy971: is it possible to to (let [arr (#^ings array)]) ?

8:31 hiredman: a type hint is not a function

8:31 leafw: killy971: see the hinting example I pasted for you above.

8:32 which should be #^ints, sorry, not #ints

8:37 killy971: great !

8:37 thank you very much

8:38 it seems that the problem came of the index variable

8:54 Jomyoot: How do I parse "(1 2 3)" into clojure

8:55 I have a String that contains Clojure Syntax -- representing a hash. How would I parse it.

8:56 tomoj: ,(read (java.io.PushbackReader. (java.io.StringReader. "{:foo 3}")))

8:56 clojurebot: {:foo 3}

8:56 tomoj: not sure if there is a shorter way

8:56 hiredman: read-string

8:56 tomoj: oh, haha :)

8:56 Jomyoot: ,(read-string "(1 2 3)")

8:56 clojurebot: (1 2 3)

8:57 Jomyoot: wow coolly

8:57 ,(read-string [1 2 3])

8:57 clojurebot: java.lang.ClassCastException: clojure.lang.LazilyPersistentVector cannot be cast to java.lang.String

8:57 Jomyoot: ,(read-string "[1 2 3]")

8:57 clojurebot: [1 2 3]

8:58 Jomyoot: ,(read-string "[\"1\"]")

8:58 clojurebot: ["1"]

8:58 Jomyoot: ooh

8:58 tomoj: that's nice cus it seems to assume the stuff is quoted, too

8:58 in my example you'd need to quote yourself

8:58 Jomyoot: what do u mean? in that I need to quote myself?

8:59 tomoj: you don't

8:59 oh actually my example is the same

9:00 read is not read and eval :)

9:00 Jomyoot: i see

9:00 but it allows me to get the structure right?

9:00 data structure

9:02 tomoj: yep

9:13 killy971: when I get a class cast exception, what does the type I am told refer to ?

9:13 the real type of the object, or the type I was trying to give it ?

9:13 to cast it to

9:16 Chouser: I think the class mentioned is what's expected.

9:16 killy971: by the way, does (int-array ...) makes an array of primitive ints ?

9:17 because somewhere in my code I was trying to cast this kind of array to #^ints, and I get a class cast exception with [I (which refer to Integer array)

9:18 Chouser: ,(int-array 0)

9:18 clojurebot: #<int[] [I@a30a4e>

9:18 Chouser: yep, primitive ints

9:27 killy971: somewhere in my code I have this : (loop [j (+ (* (* i (+ (int i) 3)) 2) 3)]

9:27 when I try to coerce j to int here with "j (int ...)", I get this :

9:27 java.lang.RuntimeException: java.lang.IllegalArgumentException: recur arg for primitive local: j must be matching primitive

9:28 I don't understand the error message

9:33 arbscht: what's in your recur form?

9:45 lisppaste8: killy971 pasted "prime sieve" at http://paste.lisp.org/display/87313

10:09 Jomyoot: Is there a good testing framework for clojure?

10:33 dnolen: jomyoot: clojure core ships with one.

10:33 Jomyoot: is it worth looking into?

10:51 clojurefirefox: what's ClassName.class in java and clojure?

10:51 dnolen: jomyoot: most definitely.

10:51 Jomyoot: i will look into it then

10:55 clojurefirefox: any diffs between Classname.class and Classname.getClass()?

10:56 classname/class not work in clojure

11:52 ambient: is Hickey's JVM summit 2009 speech available anywhere on the web? or just slides?

13:37 grosours: hi ^^

13:43 tomoj: being forced to program in python makes me crazy after clojure

13:50 leafw: tomoj: in python you also have map and reduce, and lamda.

13:51 tomoj: yes but they suck

13:52 leafw: what a great attitude. Not quite.

13:52 tomoj: maybe there is a partial somewhere?

13:53 leafw: currying ?

14:10 LauJensen: tomoj: What frustrates you about Python ?

14:12 tomoj: general ugliness, feel like I have to fight it to program in a functional style

14:12 probably just because I'm new to python

14:12 but I miss clojure :(

14:12 LauJensen: fight it?

14:13 tomoj: I mean what would feel perfectly natural in clojure feels unnatural and difficult in python to me

14:13 LauJensen: That would be beginners ticks :) But I'm considering Python for a future blogpost, so let me know if you stumble on something worth writing about

14:15 tomoj: I miss ruby blocks too

14:16 danlei: when in rome, do as the romans do ;)

14:18 tomoj: romans wiped their asses with sticks

14:19 danlei: :)

14:20 I know what you mean, but If you feel like fighting the language, you'll be better of adapting (even if it implies stick-wiping) ... ;)

14:20 (at least, if someone is supposed to read your code later on)

14:22 tomoj: yeah

14:22 luckily this class assumes no programming experience so I can get away without knowing python idiom

14:41 LauJensen: By the way, it seems clojure-concurrency is a very hot topic these days, my blog got hit with 5000 visitors yesterday, I think most of them looking at the STM vs Actor comparison

14:52 wtetzner: can you refer to the 'this' pointer in proxy?

15:57 LauJensen: So...quiet night? :)

15:58 licoresse: yes

15:58 at least the kids are asleep

16:14 dreish: It looks like synchronized () blocks don't work in Java when they occur in code in a subclass of Thread. It seems as though the JVM thinks both threads are the same because they're executing code within the same Thread object.

16:14 Either that, or I'm crazy.

16:58 LauJensen: Got an example dreish ?

16:59 dreish: Not a simple, standalone one. I'm thinking of trying to write one to see whether I'm right.

17:16 LauJensen: Apparently the "I'm crazy" theory is the more successful one at the moment.

17:16 LauJensen: dreish: That's always scary to discover :(

17:18 dreish: Well, I shouldn't be surprised. This was an over-ambitious project I started in Java several months before Clojure broke out onto the scene. I had no idea how complicated it is to do concurrency well in Java.

17:24 LauJensen: Concurrency is amazingly difficult if you haven't got Rich on your side

17:33 ,(keyword 2)

17:33 clojurebot: java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

17:33 LauJensen: Shouldn't that be implemented?

17:46 rafsoaken: could somebody explain to me what's the practical difference of (reduce + [1 2 3]) and (apply + [1 2 3]) ?

17:46 plz

17:48 Chousuke: rafsoaken: none :)

17:48 rafsoaken: + for more than two arguments uses reduce internally

17:49 rafsoaken: lol, thx - I've just seen (reduce + 1 [2 3 4]) and (apply + 1 2 [3 4]) is a difference.. the only i could spot

17:56 dreish: rafsoaken: The only practical difference I've run into is that apply will hold on to the head, so if you have a huge Seq, such as over a large file, reduce + will work and apply + will run out of memory.

17:57 rafsoaken: thanks dreish, interesting..

18:12 crios: dreish, the synchronized block should work inside a Thread subclass, too

18:12 dreish: crios: Yes, that's what I found when I wrote a small test.

18:12 crios: it depends on what you are synchronizing

18:14 the sentinel object which you synchronize on must be shared between all threads

18:15 dreish: Yes, it is.

18:18 crios: this is a simple java thread example: http://paste.lisp.org/display/87223

18:19 if it can help

19:44 danlei: (ns a) (def foo 1) (ns b) (def foo 2) (binding [*ns* (find-ns 'a)] foo) => 2. how to do it right?

20:21 dreish: danlei: (binding [*ns* (find-ns 'a)] @(resolve 'foo)) => 1

20:24 danlei: dreish: thanks

20:25 but I don't know if that will help. In my mud client, I want the users to be able to eval some code in a user package - so @(...) is not such a nice solution.

20:25 dreish: danlei: Otherwise, eval does the resolving, then calls the compiled code, which begins with binding.

20:26 Sounds like you're going to use eval anyway, so you might as well do (binding [...] (eval ...))

20:27 danlei: hm, I'll check that, thanks

20:29 ok, works

20:33 killy971: I am trying to write the atkin sieve in clojure, and I am quite lost concerning the variable scope and side-effect prevention

20:34 I declare an int-array at the beginning of my function, and I try to modify it in a for loop

20:34 but it seems that the array doesn't get modified

20:35 dreish: killy971: for is for list comprehension, not for side-effects. Try doseq

20:36 killy971: I see

20:36 thank you, I am going to try

20:37 what about "when" ?

20:37 can (when something do-side-effet)

20:37 dreish: Or you could surround the for with doall to force it to walk the entire seq.

20:39 Better yet, dorun, which won't hold the head.

20:39 killy971: can I show you my code ?

20:39 dreish: ~paste

20:39 clojurebot: lisppaste8, url

20:39 lisppaste8: To use the lisppaste bot, visit http://paste.lisp.org/new/clojure and enter your paste.

20:40 killy971 pasted "atkin sieve" at http://paste.lisp.org/display/87336

20:40 killy971: (it is not yet optimized for performance, but I first want it to return a correct result)

20:41 dreish: Where's the for?

20:41 killy971: I changed them with doseq as you said

20:42 dreish: Okay, so I'm guessing your array changes at least, right?

20:42 killy971: yes

20:48 misunderstanding

20:48 I would like the array to change

20:48 but it doesn't seem to

20:49 well it just doesn't (if you do (println (seq sieve)) just before the last loop

20:49 dreish: Maybe aset isn't being reached?

20:51 killy971: true..

20:51 sorry, I am going to investigate this way

21:03 ok... all my (rem n ...) should have been with n1, n2 and n3

21:11 thank you very much for your help

21:38 gerryxiao: hello

21:38 Chouser: gerryxiao: hi

21:39 gerryxiao: i have question about agent

21:39 how much args in agent function?

21:39 ,(def a (agent 0))

21:39 clojurebot: DENIED

21:40 gerryxiao: ,(defn f [v] (inc v))

21:40 clojurebot: DENIED

21:40 gerryxiao: (add-watcher a :sendoff a f)

21:41 it seems not work?

21:41 oops

21:41 (def b (atom 0))

21:42 (add-watcher b :send-off a f)

21:42 i got agent error

21:43 (swap! b inc)

21:43 @ a

21:43 hiredman: ,(dco agent)

21:43 clojurebot: java.lang.Exception: Unable to resolve symbol: dco in this context

21:43 hiredman: ,(doc agent)

21:43 clojurebot: "([state] [state & options]); Creates and returns an agent with an initial value of state and zero or more options (in any order): :meta metadata-map :validator validate-fn If metadata-map is supplied, it will be come the metadata on the agent. validate-fn must be nil or a side-effect-free fn of one argument, which will be passed the intended new state on any state change. If the new state is unacceptable, the validate-fn

21:43 hiredman: ,(doc send)

21:44 clojurebot: "([a f & args]); Dispatch an action to an agent. Returns the agent immediately. Subsequently, in a thread from a thread pool, the state of the agent will be set to the value of: (apply action-fn state-of-agent args)"

21:44 gerryxiao: but i change f to (defn f [v t] (inc v)), it works

21:45 at least two args? is that ture?

21:46 according doc, f with one arg should work

21:47 i have to add one useless arg

21:47 Chouser?

21:48 hmm

21:52 user=> (def a (agent 0))

21:52 #'user/a

21:52 user=> (def b (atom 0))

21:52 #'user/b

21:52 user=> (defn f [v] (inc v))

21:52 #'user/f

21:52 user=> (add-watcher b :send-off a f)

21:52 #<Atom@3cc262: 0>

21:52 user=> (swap! b inc)

21:52 1

21:52 user=> @a

21:52 java.lang.Exception: Agent has errors (NO_SOURCE_FILE:0)

21:55 hiredman: ,(doc add-watcher)

21:55 clojurebot: "([reference send-type watcher-agent action-fn]); Experimental. Adds a watcher to an agent/atom/var/ref reference. The watcher must be an Agent, and the action a function of the agent's state and one additional arg, the reference. Whenever the reference's state changes, any registered watchers will have their actions sent. send-type must be one of :send or :send-off. The actions will be sent after the reference's state is

21:56 hiredman: it doesn't say, but I would imagine the new value for the ref is also passed to the agent action

21:56 ah

21:57 it does actually say

21:57 "The watcher must be an Agent, and the action a function of the agent's state and one additional arg, the reference."

21:59 gerryxiao: (clear-agent-errors a)

21:59 (remove-watcher b)

21:59 (defn f [v n] (inc v))

21:59 hiredman: you don't need to paste every line like that

22:00 gerryxiao: (add-watcher b :send-off a f)

22:00 (swap! b inc)

22:00 hiredman: lisppaste8: url

22:00 lisppaste8: To use the lisppaste bot, visit http://paste.lisp.org/new/clojure and enter your paste.

22:00 gerryxiao: @a

22:00 1

22:00 hiredman: if you are having trouble with some code, paste it, togther with the exception you are getting, into the pastebin

22:00 gerryxiao: it works, but why?

22:00 hiredman: gerryxiao: are you kidding?

22:01 read the docs for add-watcher

22:01 read the line I quoted from the docs for add-watcher

22:03 gerryxiao: anyone here?

22:04 hiredman:

22:19 gerryxiao: so i have to pass ref as second arg?

22:21 ok

22:31 sure ,the second arg is the ref

22:32 but i dont want use it in my case

22:33 :)

22:42 interferon: i'm a clojure beginner and need help cleaning up an ugly piece of code. i want to accept new connections on a ServerSocket and fire them off to a thread:

22:42 (loop [socket (.accept server)]

22:42 (. (Thread. #(process-request socket)) start)

22:42 (recur (.accept server)))

22:42 is there a better way to write that loop without the repetition of the accept call?

22:44 manic12: isn't loop all about repetition?

22:45 interferon: yes, but i'm talking about typing it twice

22:45 i want something similar to the while( (socket = server.accept() ) ) idiom in other languages

22:46 Chouser: just use a 'let' inside the loop

22:47 jamesswift: can anyone suggest a simple lib or sample code to graphically display a list as a tree? thanks.

22:47 Chouser: (loop [] (let [socket (.accept server)] ...))

22:47 interferon: nice

22:47 like

22:47 (loop []

22:47 (when-let [socket (.accept server)]

22:47 (. (Thread. #(process-request socket)) start)

22:47 (recur)))

22:48 Chouser: interferon: sure. Next time use pastebin for more than a couple lines.

22:48 manic12: <hand slap>

22:49 killy971: it is really hard to get good performance with clojure while keeping code clean

22:49 interferon: where is pastebin?

22:49 manic12: killy971: use c++, is that clean enough?

22:49 Chouser: lisppaste8: url

22:49 lisppaste8: To use the lisppaste bot, visit http://paste.lisp.org/new/clojure and enter your paste.

22:49 killy971: I want to use clojure

22:50 but would like not to have to cast ints everywhere just because it's faster

22:50 manic12: anytime you optimize code it becomes less clean

22:50 jamesswift: sometimes it becomes cleaner ;)

22:51 manic12: unless you wrote the algorithm wrong to begin with

22:51 interferon: killy971: do you really "have" to use ints? and how is that less clean than other languages which will probably always require type declarations?

22:51 killy971: well

22:51 type declaration is only to be used once

22:51 manic12: symbolics didn't need declarations

22:52 Chouser: clojure is young. have patience.

22:53 manic12: i've been doing all this stuff with native calls from clojure, and I had three major bugs, and they were all in the clojure code ;)

22:57 i found bugs in msvc++ debugger though. imagine that.

23:10 lisppaste8: jamesswift pasted "tree transform" at http://paste.lisp.org/display/87340

23:11 jamesswift: anyone want to help my tired brain out and describe how they might transform the inTree to the outTree?

23:11 years of imperative is making it hard to describe algorithms functionally

23:13 should be simple though, right?

Logging service provided by n01se.net