Fork me on GitHub

I am developing a small app. However, I discover that the API supports JSON-LD, therefore you receive data that is similar to {"answer1!: "ans", "@type": "somevalue"}. This @ reflect the linked data. I have worked around the @type for now however this is not suitable. What do folks do? Anyone have a view or point me in the right direction? BTW the only clojars JSON-LD is for OWL implementations

Drew Verlee13:05:29

What's the goal & problem? Based on a quick scan of the idea of JSON-LD it's a form of sending links. Depending on your goal, you could just ignore the links. e.g i ask for a persons name and it sends + name = "john" + person = some-unique-url


Hi @U0DJ4T5U1, this is a part of a larger JSON-LD, when map is prepared as part of json/read-str, the map is faulty as included is the {:@type spmevalue} One cannot use the map as the REPL complains due to the @type symbol. S I did a hack, however is there a better way of dealing with this as in some industries have gone JSON-LD mad (driven by MongoDB for instance) and will be more prevalent in due course.

Drew Verlee13:05:51

Json-ld is valid json right? > One cannot use the map as the REPL complains due to the @type symbol. > json/read-str, the map is faulty as included is the {:@type spmevalue} are you using clojure.json/read-str with the key convertor function to a clojure keyword. eg

(json/read-str "{\"a\":1,\"b\":2}"
               :key-fn keyword)
;;=> {:a 1, :b 2}
if so, then maybe dont include :Key-fn keyword and let it remain a string.

Drew Verlee13:05:35

strings are valid keys in a clojure map so {"@type" <some-value>} is valid clojure.


That is what I did; I processed the string and substituted the @keys and then continued. Nevertheless, this will happen more and more as companies adopt JSON-LD


strings are valid, however to maniplate or search a complex structure keywords as strings are problematic


the keyword function happily makes a keyword out of any string

scratch=> (keyword "@anything")


any string can be a key in a hash-map, you don't need to create keywords


the only actual problem is attempting to use invalid keyword literals in your code, but nothing forces you to do that


keywords aren't magic, they are a convenience, you can simply not use them in cases where they cause complexity


I have had problems when search complex structures which have maps and vectors in them - then it is a problem


you can sue the get and get-in functions with strings, you don't need keywords


the json conversion doesn't need to create keywords, that's optional


yes, I used get-in but have experienced problems - change to keywords (not incl :@type) then OK


you don't need keywords

scratch=> (get-in {"a b c" {"d e f" 2}} ["a b c" "d e f"])


I agree you can use strings for keywords


for me just makes it more unreadable in complex structures

Kira McLean12:05:08

Hi folks, I’m wondering if this is the right place to ask about something I’m not understanding that seems really basic. Please let me know if this is the wrong place for my question. I wrote a tiny bit of clojure that crawls a directory and converts markdown files to html files, and it works fine. I decided to try to write a test for it and I was surprised by what I’m seeing. Calling the code from the test doesn’t appear to write all the files. Am I missing something obvious about how clojure tests work? I was thinking maybe the test is finishing before the whole directory is crawled? I was thinking waiting a bit in the test would give me expected results if that was the case, though, which it doesn’t. I’m having a hard time debugging it to see what’s going on. I’m assuming I’m misunderstanding something about how clojure works with files. I’m not looking for someone to figure out exactly what my problem is, but would appreciate any insight in case this is a problem that is immediately obvious to experienced clojurists. I’ll add code to this thread in case that’s helpful.. I’m not always great at explaining things with words!

Kira McLean12:05:45

This is the code:

(ns site.core
  (:require [datoteka.core :as fs]
            [site.markdown :as md]))

(defn- process-content [content]
  (md/->html content))

(defn- mirror-input-to-output [{:keys [input-dir output-dir]} node]
  (->> input-dir
       (fs/relativize node)
       (fs/path output-dir)))

(defn- process-file [context node]
  (->> node
       (spit (mirror-input-to-output context node))))

(declare walk)

(defn- process-directory [context node]
  (->> node (mirror-input-to-output context) fs/create-dir)
  (map (partial walk context) (fs/list-dir node)))

(defn- walk [context node]
  (if (fs/directory? node)
    (process-directory context node)
    (process-file context node)))

(defn generate-site [context]
  (walk context (:input-dir context)))

(defn -main [context]
  (generate-site context))
And this is the failing test I was expecting to pass:
(ns site.core-test
  (:require [clojure.test :refer [deftest testing is]]
            [site.core :as sut]
            [ :as io]))

(def input-dir "test/site/resources/content/")
(def output-dir "test/site/resources/site/")

(deftest copies-files-to-the-right-place
  (testing "for now just copies everything into the output directory"
    (sut/generate-site {:input-dir input-dir :output-dir output-dir})
    (let [result (->> output-dir io/file file-seq (map str)) ]
      (is (= ["test/site/resources/site"


Hi @kiraemclean, drop a snippet here and I'll have a looksee. I'm not the most experienced, but definitely happy to help.

Kira McLean12:05:06

Amazing, thank you! I added a snippet in the thread. I’m realizing that’s a painful way to view code, though. Snippet incoming..

Drew Verlee12:05:21

its readable.


in process-directory you want to put a doall around your call to map -- map returns a lazy sequence so it won't be fully realized (and won't perform any side effects) until you do something with all the values it produces. It works fine when you return it from -main because that forces it to realize the lazy seq

Drew Verlee13:05:47

That's probably it. This would be confirmed by the test failure output right?

Kira McLean13:05:48

Ah makes sense! I am constantly getting bitten by laziness. It’s obviously a great thing, just need to adapt to it.

Kira McLean13:05:46

That works, thanks so much for your help! Also glad it was obvious!


yeah, it's very common to use (doall (map ,,,)) for side effectful stuff, just becomes something you get used to seeing and writing 😄

Kira McLean13:05:30

Makes sense.. I need to mentally adjust to the concept of side effects being fundamentally separate from how data flows through a clojure program.

Kira McLean13:05:20

It’s really cool that it works this way, but takes some getting used to.


you may want to separate file processing from directory traversal @kiraemclean


file-seq, a function in clojure core, gives a directory traversal


you can filter it for files you're interested in and process

Kira McLean13:05:05

yeah that seems like a good idea.. at first I had a collection of something like [{:path ,,, :content …}] and writing them all at once at the end but went with this way instead because it seemed simpler. But I’m already seeing complications from mixing the two things.. testing would be much simpler if I were just checking a data structure like that.

Kira McLean13:05:33

Anyway, thanks everyone for the help, can’t believe how fast you are! So far I’m really loving clojure, a big part of it is the community.. have only encountered friendly and helpful folks so far 😄

Kira McLean13:05:06

Hope you all enjoy the rest of your weeks!


I’m having a bit of a hard time wrapping my head around zippers. I want to model my part of my application state as a literal dialogue tree that the user can navigate in back and fourth by a pointer to the data structure, ie, sound like a case where zippers would work very well. In the example the current pointer to the tree is highlighted in black, and the view would display the current node along with its ancestors. But I’m having some troubles building the tree from nothing. All of the writing I can find on the subject deals with navigation and manipulation of existing trees, not construction of one. I want to make a zipper from a base ["prompt"] and then append three children of some string, but using append child seems to add them to the same level. I’m guessing I’m having some troubles with my conceptualisation of zippers, but if someone has any pointers i would really apprech!

Akshay C. Gollapalli20:05:41

Not sure if anyone replied to you, but arguably the best way would be to just have nested vectors and then call vector-zip over it like

[:prompt [:route-1 ...]
          [:route-2 ...]
          [:route-3 ...]]
You could use keywords, and then map them to dialogue options, accompanying image, etc. like so:
{:prompt ["Welcome to my RPG" image1.jpg]
 :route-1 ["Antagonist: Oh... you're approching me?" image2.jpg]
 :route-2 ["Love interest: It.. It's not like I'll miss you, or anything" image3.jpg]
 :route-3 ["Hero: Just who do you think I am?" image4.jpg]}

Joshua Derocher-Vlk16:05:55

Hello! I am new to clojure! I have a background in JavaScript using libraries like Ramda and Crocks. I am trying to branch out into other functional languages in the hopes of someday being able to use one in my day job.

👍 8
👋 12

Welcome! I started from a similar background, using Ramda in JS made me look for more functional languages and I ended up with Clojure

Joshua Derocher-Vlk16:05:15

The idea of being able to use clojurescript is on the front end and clojure on the back end is very appealing to me


Yeah, it’s good to be able to do full stack with one language

Vishal Gautam17:05:26

You can run clojurescript on the backend too:

Joshua Derocher-Vlk17:05:34

Oh, interesting. What would be the reason to do that?


I guess using some javascript library you really need and has no alternative on the clojure world

Vishal Gautam17:05:25

If you are a NodeJS dev and want to explore what cljs has to offer on the server side


Could also seeing it being useful in the AWS lambda world where you want JS as your runtime language for a cold-start sensitive endpoint


What is the current best-practice for telling lein to depend on a git repo (a temporary fork of the real dependency)?


using checkouts


it's kinda fiddly to get right, but it does work


you're welcome

Mark Wardle18:05:49

Hi all. Sorry for a beginner question, but in clojure.spec.alpha, I was expecting explain-data to include the path of missing keys, in order to highlight the appropriate HTML elements in a form. As per the example on (s/explain-data ::person {::first-name "Bugs"}) => {:cljs.spec.alpha/problems ({:path [], :pred (cljs.core/fn [%] (cljs.core/contains? % :cljs.user/last-name)), :val {:cljs.user/first-name "Bugs"}, :via [:cljs.user/person], :in []} {:path [], :pred (cljs.core/fn [%] (cljs.core/contains? % :cljs.user/email)), :val {:cljs.user/first-name "Bugs"}, :via [:cljs.user/person], :in []}), The :path is empty. Is there an idiomatic way of identifying missing keys programmatically? It would seem very wrong to try to parse the predicate. I guess I could merge a map without missing keys but with nil values and that would give me the paths of missing keys (empty fields), but that feels very wrong too! It does at least mean I can drill into the problems.path and get a list of the problem keys…. Is there something in the API I am missing? All advice much appreciated! Thank you! (s/explain-data ::person (merge {::last-name nil ::first-name nil} {::first-name "Bugs"})) => {:cljs.spec.alpha/problems ({:path [], :pred (cljs.core/fn [%] (cljs.core/contains? % :cljs.user/email)), :val {:cljs.user/last-name nil, :cljs.user/first-name "Bugs"}, :via [:cljs.user/person], :in []} {:path [:cljs.user/last-name], :pred cljs.core/string?, :val nil, :via [:cljs.user/person :cljs.user/last-name], :in [:cljs.user/last-name]}), ......


@mark354 What we do at work is parse the :pred expression

👍 4
Mark Wardle18:05:48

Thank you. I suppose it is unlikely that the auto-generated predicate would change for a (contains?) check anyway. I suppose it is a self-inflicted problem as a proper old fashioned HTTP POST would send nil values to a server generating the next page while instead this new-fangled front-end cljs ‘fake submit’ malarkey generates a map with missing keys unless the user does something to trigger on-change actions. Thank you for your help.


Also, there is absolutely no reason to apologize for asking beginner questions on the #beginners channel -- you are in the right place 🙂

👍 8
💯 4
Lennart Buit18:05:24

There is also a tiny library, phrase, that makes parsing of those preds easier


And Expound is another one that turns explain-data into human-readable messages...

👍 4
Mark Wardle20:05:30

Thank you - I’ll check them both out! Might have known that it’d already be solved!

Alex Miller (Clojure team)21:05:27

Hi all, I'm doing a talk Thu night (US Central time) at our local Java user group about Clojure. This is intended for people new to Clojure, particularly those coming from Java. RSVP here if interested to get access: Note - if you are familiar with Clojure already, you will not find this useful and the number of spots is limited, so please let others join instead

👍 56