Learning Clojure: comparing with Java streams
blog.frankel.ch
blog.frankel.ch
(->> justice-league
(map #(:vehicles %))
(filter #(not (nil? %)))
(flatten))
can be simplified to just `(mapcat :vehicles justice-league)`- Keywords (i.e. `:vehicles`) can be used as getter functions, as covered in the OP.
- mapcat (like most collection functions in clojure) treats `nil` as an empty sequence
We have a programmer with >15 years experience, who is learning a new language and in the process producing code that is neat, functional (in the sense of no bugs) and not unreasonably verbose. Clojure is a language that is dense with clever ideas, it takes a lot longer than a few months to get good at using them if they are new. The terse comments throwing out improvements or making corrections seem a little harsh, I'd hoped to see people talking more about concepts than naming functions.
Actually, I didn't feel any comments were particularly harsh. I'm learning from most of them. This is exactly the feedback I'm searching for: idiomatic Clojure.
With the caveat that `flatten` is recursive and can handle a mix of collections and non-collections, while `mapcat` is non-recursive and every value returned by the mapcatted function must be a collection or nil.
(Identity returns its argument.)
(flatten [1 [2 3] [[4 5 6]]])
> [1 2 3 4 5 6]
(mapcat identity [1 [2 3] [[4 5 6]]])
> IllegalArgumentException Don't know how to create ISeq from: java.lang.Long
(mapcat identity [[1] [2 3] [[4 5 6]]])
> (1 2 3 [4 5 6])
(flatten [nil [1 2 3]])
> (nil 1 2 3)
(mapcat identity [nil [1 2 3]])
> (1 2 3)From this I provisionally conclude that "flatten" being non-recursive is an artifact of Scala confusing things; not an unusual thing in my experience with this language, but this time it seems to have gotten out of hand, instead of being contained within Scala ecosystem.
--
[0] - Even Haskell calls this `concatMap`, see [1].
[1] - https://stackoverflow.com/questions/49843262/where-does-the-...
Erm, what? I am confused by the use of the word 'limited' in this context.
A good example of code with macro overkill is compojure-api. There is just so many clever macros that you need to read the original source code with a microscope to know what the code expands to.
I feel reader macros have the problem that it modifies/extend the foundational syntax. And that can become overloaded really quickly. Normal macros can change evaluation semantics, but not the fundamental syntax. Which makes it easier to navigate and understand in general. Also, reader macros are pretty hard to write in comparison.
Racket has a nice take on them though. Making them more like all new programming grammars, and defining your dialect per file. Even there though, most alternate syntax is for research or fun mostly.
Scheme even has a fixed syntax in the RnRS reports. Lisp does not have that.
> Also, reader macros are pretty hard to write in comparison.
Not really. One just reads things from a stream and returns an s-expression.
> I feel reader macros have the problem that it modifies/extend the foundational syntax
That's a feature.
The main purpose of user-written reader macros is to extend S-expressions to support literal syntax of new user-defined data types.
There is nothing scary about it at all.
> Racket has a nice take on them though. Making them more like all new programming grammars, and defining your dialect per file
My Lisp Machine had that already in the 80s. It supported different language syntax - from various Lisp variants to Pascal, C and other languages.
> Even there though, most alternate syntax is for research or fun mostly.
On my Lisp Machine it was actually useful.
Could you link to the RnRS in question? I'm curious.
> Not really. One just reads things from a stream and returns an s-expression.
I feel like if I agree to this, I also agree that pre-processors are easy to write and use. And that undermines the innovation and superiority of syntactic macros introduced by Lisp.
> The main purpose of user-written reader macros is to extend S-expressions to support literal syntax of new user-defined data types.
This is possible in Clojure as well. You can extend the language with new data literals. But they must begin with # + char(s) and respect the existing reader semantics, as they are processed as a syntactic macro, and not a lexical one. So you could add #queue[1 2 3] for example. But you couldn't add |1 2 3|
> On my Lisp Machine it was actually useful.
If only they'd still make those, and had improved its performance.
https://schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-...
> And that undermines the innovation and superiority of syntactic macros introduced by Lisp.
They have a different purpose and operate on a different level.
> respect the existing reader semantics
This is an example of xapping data structure in *Lisp for for the Connection Machine
{moe->"Oh, a wise guy, eh?" larry->"Hey, what's the idea?" curly->"Nyuk, nyuk, nyuk!"}
> If only they'd still make those, and had improved its performance
Nowadays it might be easier to use an emulator running the OS.
Sorry, maybe I'm not seeing it, but what part of that link describe lexical user definable reader macros in scheme?
> {moe->"Oh, a wise guy, eh?" larry->"Hey, what's the idea?" curly->"Nyuk, nyuk, nyuk!"}
Right, so in Clojure you could extend the reader and do:
#xapping{moe "Oh, a wise guy, eh?" larry "Hey, what's the idea?" curly "Nyuk, nyuk, nyuk!"}
As a data literal for xapping data structures.
None. I never said it does. I said it has a fixed syntax.
What do you mean by fixed syntax? A formal syntax?
No pun intended, by "value" I mean "things that are worthwhile". Clojure is totally into "values". :-D
(first (map #(extract-name %) justice-league))
Does it map all the elements or only the first? This is actually the key aspect of streams, and more important in many ways than whatever syntax sugar is available to invoke the operations.