Hi Christian,

You are looking for "into", which is already part of the Clojure standard 
library.

Appending:

(into '(1 2) '(3))         ;=>  (1 2 3)
(into [1 2] [3])           ;=>  [1 2 3]
(into {:a 1 :b 2} {:c 3})  ;=>  {:a 1, :b 2, :c 3}
(into #{:a :b} #{:c})      ;=>  #{:c :b :a}

Prepending:

(into '(1) '(2 3))         ;=>  (1 2 3)
(into [1] [2 3])           ;=>  [1 2 3]
(into {:a 1} {:b 2 :c 3})  ;=>  {:a 1, :b 2, :c 3}
(into #{:a} #{:b :c})      ;=>  #{:c :b :a}

The "into" function pours the contents of the second collection into the 
first collection, returning a collection of the same type as the first 
argument.

That being said, I agree with Alex and James in this rather lengthy 
discussion. Clojure is unique among the Lisps in that it moved beyond 
having linked lists as the only first class data structure.

Prior to Clojure, if you worked in a Lisp like Scheme or Common Lisp, you 
would design your program around the creation, traversal, and manipulation 
of linked lists using higher order functions and explicit recursions. The 
standard library in both languages is heavily focused on these list-related 
operations. After developing the initial version of your program, if you 
found that it was too slow or used too much memory, the accepted practice 
was to profile your application to identify the functions that were getting 
hammered the most and were using up the majority of your computing 
resources. You would then often end up rewriting those function bodies in 
an imperative fashion using loops and mutable data structures (i.e., arrays 
and hashtables). The "wisdom" here was that this would enable you to "first 
make it right, then make it fast". If even further performance was required 
from your program, you might then rewrite part of your program in C, build 
a foreign function interface (FFI) to link the C code into your Lisp 
program, and go from there. These were the Bad Old Days of Lisp(TM).

What was IMHO quite possibly Rich's greatest contribution in the design of 
Clojure to the Lisp world was his decision to make additional data 
structures first class citizens of the language. Most importantly, he did 
so by creating Clojure's vectors, maps, and sets to be immutable, 
persistent, performant, recursively constructed, and representable as data 
literals. This was already a wonderful improvement over previous Lisps, but 
it created a new problem: How could we enjoy the pure delight of 
list-oriented programming that Lisp had always offered us now that the data 
structure space had been fragmented? A famous quote from Alan Perlis is a 
popular gem in the Lisp world, and it goes like so:

"It is better to have 100 functions operate on one data structure than to 
have 10 functions operate on 10 data structures."

Every Lisp had always satisfied this by simply giving programmers only one 
first class data structure to use: the linked list. As I already mentioned, 
the bulk of its standard library would then be built around list 
manipulation functions. Clojure needed a way to preserve this unified style 
of programming while still providing a collection of performant data 
structures for real-world programming. So how did Rich accomplish this?

He created the "sequence abstraction". A sequence in Clojure serves a 
similar role to the linked list of previous Lisps in that it unifies the 
API for interacting with all of Clojure's first class data structures 
(list, vector, map, set). By calling the function "seq" on any data 
structure, you are given a list-like view of that collection that allows 
you to traverse it from beginning to end one element at a time and to add 
new elements to the beginning of it. These operations are called "first", 
"rest", and "cons", and they behave precisely as you would expect them to 
if you were calling them on a linked list.

By using seq throughout the Clojure sequence library (i.e., the set of 
standard library functions responsible for creating, traversing, 
transforming, and manipulating sequences), Clojure is able to have single 
implementations of all of the common Lispy higher order list transformation 
functions. For example, we have "map", "filter", "reduce", "iterate", 
"take", "drop", "repeat", "cycle", and so on. The amazing thing is that 
these can all take any of Clojure's data structures as their inputs. So you 
can call map on a list, vector, map, or set without having to change the 
function signature. Without the sequence abstraction, we could need 
multiple functions for every data structure we wanted to support (e.g., 
map-list, map-vec, map-hash, map-set, filter-list, filter-vec, filter-hash, 
filter-set). This is precisely the kind of combinatorial explosion of the 
function space the Alan Perlis was warning us about. The tradeoff is that 
each of these higher order functions will then return a new sequence as its 
output. While this prints to the REPL like a list, please note that a 
sequence is not a list (except when it is a sequence on a list ;-D ). It is 
a list-like representation of the contents of any data structure. You can 
check this by calling the "type" function on the output of either "seq" or 
any higher order function (e.g., map, filter, reduce) that calls seq 
internally.

So when you are programming Clojure or teaching it to new programmers (as I 
have done on numerous occasions), it really is important IMHO to take a 
moment to appreciate the history that motivated Rich's design decisions 
around data structures and the sequence abstraction and not to simply write 
it off and treat Clojure as though it were Scheme or Common Lisp made to 
run on the JVM.

In Clojure, the choice of your data structures is central in the design of 
your programs when it comes to performance. However, an equally important 
part of program design is the conceptualization of much of your program as 
a series of sequence transformations composed together so as to reach the 
output you desire from the inputs you are given. To that end, if you wish 
to equip new programmers with the skills to think like a Clojure 
programmer, I would first teach them the four main data structures (list, 
vector, map, set) and the functions to operate on each of them. Next, I 
would teach them the sequence API and demonstrate how these four data 
structures are represented as sequences. This enable everyone to reason in 
a straightforward manner about all of the sequence functions going forward. 
Then, I would teach them how to use higher order functions like map, 
filter, reduce, and range to replace loops and mutation in their program 
logic. After this, I would discuss recursion and function composition as 
the fundamental components of flow control in a functional programming 
language. Finally, I would spend some time going over dynamic vs lexical 
scoping rules, shadowed bindings, namespaces, and the call stack.

This should provide your students with most of the groundwork that they 
need to get going with Clojure programming and to dig deeper into various 
advanced topics like host interop, concurrency primitives, parallel 
programming, spec, pure/impure functions, macros, and so on.

One thing that I would definitely avoid in teaching a new language is to 
alter the syntax of that language on day 1 and teach constructs that are 
neither efficient nor particularly useful in practice. To that end, I would 
advise you to use the "into" function that I demonstrated at the beginning 
of this email if you wish to teach a unified API for appending and 
prepending to each of Clojure's four main data structures while preserving 
the types of the function's inputs.

And with that, I'm going to head back to my day job. Good luck in learning 
Clojure and teaching it to others, and don't hesitate to reach out with 
questions to the Clojure mailing list. Most of the folks on here are 
usually very friendly and intelligent, and I've always found that to be a 
hallmark of this community.

Happy hacking,
  Gary

-- 
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 from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to