[HN Gopher] Data notation in Clojure
___________________________________________________________________
Data notation in Clojure
Author : wernerkok
Score : 88 points
Date : 2021-06-30 07:23 UTC (1 days ago)
(HTM) web link (ostash.dev)
(TXT) w3m dump (ostash.dev)
| geokon wrote:
| Maybe I'm wrong, but I don't think EDN really has anything to do
| with homoiconicity. You're just parsing in data (strings) into
| other data (internal Clojure data structure). It's nice that the
| syntax matches how you'd write the data structure in Clojure
| itself.. but you could also parse EDN in a non-homoiconic
| language.
|
| Clojure's lispyness is a bit overplayed? I feel it's generally
| irrelevant for the vast majority of language users and it's
| really not what makes the language great. I seldom seeing custom
| macros... and while I'm sure it's important for a minority and
| I'm glad it's there - if it was disabled tomorrow (outside of the
| language internals) I'd probably take me a while to even notice
| :)
| Blikkentrekker wrote:
| Custom macros are very commonly exported by libraries, but it
| also has nothing to do with homoiconicity, _Rust_ supports a
| fairly similar procedural macro model as well as a more higher
| level pattern matching one and isn 't homoiconic.
|
| A macro system that manipulates the syntax tree directly has
| little to do with homoiconicity.
|
| The real advantage is simply that the syntax is very simple and
| consistent but there is no need for it to be homoiconic to be
| that.
| adrusi wrote:
| There are other advantages. I occasionally find myself using
| syntax-quote for refactoring. Let's say I have a test like
| this: (let [cases [{:in [1 2 3] :out [2 4 6]}
| ;; ... ]] (doseq [{:keys [in
| out]} cases] (is (= out (test-fn in)))))
|
| If I want to inline the test cases, I can rewrite it like this
| (let [cases [{:in [1 2 3] :out [2 4 6]} ;;
| ... ]] (map (fn [{:keys [in
| out]}] `(is (= ~out (test-fn ~in))))
| cases))
|
| Then eval that in the repl and copy the result back into the
| test file. It's a bit clunky because the repl output uses fully
| qualified symbols, but cleaning that up is usually faster that
| manually typing out the code it generates.
| blacktriangle wrote:
| The thing about macros, it's not about day to day, or even
| month to month, macros are about year to year. Macros are what
| let programers expand the language in ways the designers never
| even considered, thus increasing the timescale over which your
| code is maintainable.
|
| For a recent example of why you wish your language had macros,
| look at function builders in Swift. When SwiftUI was first
| announced its syntax was a huge wtf, only later did we find out
| that a new language feature was enabling this syntax.
|
| Now imagine if somebody outside of Apple had wanted to do this
| and had to convince the Swift dev team to incorporate this
| feature...good luck.
|
| In any given lisp, function builders are a 10 minute project to
| make a macro.
| bjoli wrote:
| CLOS was bolted on to a lisp a very long time ago, in a
| galaxy far far away. It felt and looked like a first class
| feature of the language.
| dustingetz wrote:
| Clojure's lispyness is massively underrated. At Hyperfiddle we
| have a fully reactive Clojure/Script dialect, backed by a
| functional effect system https://github.com/leonoel/missionary.
| Our reactive clojure can also distribute across the
| client/server system, like distributed map reduce. Imagine
| React.js but full stack, incremental view maintenance all the
| way from database views to DOM views as one streaming
| computation. We implement a custom analyser like core.async for
| full compatibility with host clojure, including preexisting
| macros, so it gets destructuring, control flow, higher order
| logic, it's a first class Clojure in every way. What you are
| measuring I think is that metaprogramming is hard, the books
| and papers about it are hard, the intersection of
| metaprogramming with functional programming isn't even well
| understood in research, and businesses don't invest in hard
| things so you won't get paid to learn any of this.
| hellomotomoto wrote:
| > incremental view maintenance all the way from database
| views to DOM views as one streaming computation
|
| swoon!
| sova wrote:
| Macros are indispensable for making libraries! Take a look at
| the source code of any one of the libraries you use on a daily
| basis and search for "defmacro" and you might be shocked =)
| keymone wrote:
| yes, it's just a data format that can be recursive. it should
| really replace all the json, yaml, toml and everything else.
| after using edn i simply hate every other format, they are
| painful :(
| ARandomerDude wrote:
| > Maybe I'm wrong, but I don't think EDN really has anything to
| do with homoiconicity. You're just parsing in data (strings)
| into other data (internal Clojure data structure).
|
| Part of the very real value in being able to print the data
| exactly as you'd type it in as a literal is repl-driven
| development and production logging. I can effortlessly print
| something to the production logs, copy/paste it into my
| development environment, and debug. So while it's true that
| string (de)serialization is an old idea, very few languages
| support one-to-one copy/paste (de)serialization. So often,
| other languages give you <Object_0xffa003> or something if you
| try to call x.toString(). Clojure makes it easy to move data
| around between systems, by emphasizing extensible, yet simple
| data structures.
| frompdx wrote:
| I understand what you are trying to say regarding macros and I
| think you are correct that many probably do not define their
| own macros and happily use the language as is. However, I think
| if macros are disabled everyone will notice right away.
| (macroexpand '(defn foo [x] (println x)))
|
| Returns: (def foo (clojure.core/fn ([x]
| (println x))))
|
| Clojure itself is built from macros. Even something simple like
| defining a named function with defn.
| geokon wrote:
| Maybe I should have left off the last part b/c it's a bit
| provocative :) Yeah, the language itself is built on macros -
| but as a language user you don't really care as it's an
| implementation detail. I guess my point is that you could
| know nothing about macros and still use Clojure very
| productively. Selling Clojure as another Lisp feels like sell
| a car for its cupholders. The data structures, syntax, JVM
| interop, REPL etc. are more interesting and have little to do
| with Lisp. But that's just my opinion as a casual user
| blacktriangle wrote:
| But the reason you care is because the language is built
| out of the same machinery that you the developer have
| access to. It's not only an implementation detail because
| you have access to it as well.
| ollran wrote:
| What's the story behind clojure.core/read-string? I have always
| wondered why it executes code. There are some examples in the
| docs: https://clojuredocs.org/clojure.core/read-string
| alex_stoddard wrote:
| I haven't fully grokked it myself. But it relates to the
| concept and implementation of a Lisp reader.
|
| https://en.wikipedia.org/wiki/Lisp_reader
|
| "Unlike most programming languages, Lisp supports parse-time
| execution of programs, called "read macros" or "reader macros".
| These are used to extend the syntax either in universal or
| program-specific ways."
|
| read-string is a convenience over calling read itself as it
| will take a string rather than needing you to create a
| java.io.PushbackReader yourself.
|
| As such read and read-string were implemented as core to
| Clojure's self interpretation and implementation, not as part
| of a general safe serialization API.
|
| Unfortunately the temptation was to reach for read-string for
| de-serialization in general as it was so convenient and in the
| core. In a dev setting where you control and trust all input
| that is fine. In other contexts it definitely is not!
| ollran wrote:
| Oh yes, reader macros, now I get it. As far as I know, you
| cannot write your own reader macros in Clojure (unlike in
| Common Lisp).
| drcode wrote:
| Yes you can, but they are a bit constrained in what they
| can do. For instance, it's easy to write a debug macro that
| prints the intermediate value of b+c:
|
| (let [a #d (+ b c)] ...)
|
| So the #d reader macro has access to the following form,
| but gets ignored at a higher level (i.e. when the "let"
| form is processed)
| adrusi wrote:
| The clojure reader supports the #= prefix before a form, which
| will cause the reader to read the following form, pass it to
| eval, and use the result as if it were part of the passed in
| data. (def #=(symbol "foo") :bar)
|
| Gets read as (def foo :bar)
| wtetzner wrote:
| Yeah, so someone could provide a malicious input like:
| (read-string "#=(launch-the-missiles)")
| [deleted]
___________________________________________________________________
(page generated 2021-07-01 23:01 UTC)