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