Brilliant exposition, thanks!

On Fri, Jul 20, 2018, 10:51 AM Gary Johnson <lambdatro...@gmail.com> wrote:

> 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.
>

-- 
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