Fork me on GitHub
#beginners
<
2021-07-22
>
Andrew Berrien00:07:00

Does anyone know how I can get syntax highlighting working in vim/neovim? First I tried everything according to this article: https://oli.me.uk/getting-started-with-clojure-neovim-and-conjure-in-minutes/ and still didn't get full highlighting, so then I tried fireplace + vim-clojure-highlight but honestly nothing changed after installing it. After both tries, I still don't get any highlighting on any imported functions (str/split) or functions defined within the same file. Even when they come immediately after open parens like (my-func xyz). Atom does this with no REPL and zero plugins, so I'm guessing this isn't a hard task. But I still can't seem to get it. Any advice?

seancorfield01:07:29

There's a #vim channel that might be able to help @U0292NREWDP

lispyclouds07:07:14

Which vim/neovim version are you using?

Andrew Berrien18:07:07

@U7ERLH6JX Currently using Neovim 0.5

lispyclouds18:07:32

do you have the following in your init.vim?

filetype plugin indent on
syntax on
if youre using vimscript to configure?

lispyclouds18:07:40

if you have your config checked in somewhere, im happy to look at it as well 😄

lispyclouds18:07:31

also if you're new to vim, let me know too and probably better to move it to the #vim channel like Sean mentioned, more of us can help you along better! 😄

Andrew Berrien19:07:02

@U7ERLH6JX Thanks, I will post to #vim channel

Rob Haisfield00:07:28

Anyone know a good library for working with Zotero?

seancorfield01:07:36

And still no answers 🙂

seancorfield01:07:50

What is Zotero? I've never heard of it.

sova-soars-the-sora04:07:05

"Zotero is a free and open-source reference management software to manage bibliographic data and related research materials." So not LaTeX but something like it?

seancorfield05:07:02

Based on this it looks like it has a basic REST API so you could use any HTTP client library in Clojure to interact with it: https://www.zotero.org/support/dev/web_api/v3/basics -- we use httpkit at work because I like the simplicity of it (no troublesome dependencies, and the client is async by default).

Rob Haisfield12:07:54

Sorry @U04V70XH6 if it’s frowned upon to ask in multiple channels! I figured I would re-ask in this channel because you said a while ago something about how people are more open to questions here because that’s what they sign up for in this channel

Rob Haisfield12:07:10

Okay I’ll try the HTTP stuff

Rob Haisfield12:07:33

My context of use is that I’m working on a research project where we have a ton of markdown notes and a ton of papers. Zotero lets me attach metadata to those papers automatically and intentionally and stores all of that in a database. I wanted to see if there was some way to programmatically work with all of that data from Clojure since I know there is an extensive developer community for Zotero in general.

Rob Haisfield12:07:27

Btw Sean the stuff you were helping me with yesterday with the regex is helping me write a custom search engine for my MD notes :)

indy13:07:50

Zotero will be able to export csvs or JSON I’m assuming. It’s a very helpful tool which I too used to use when I was doing my research.

Rob Haisfield13:07:25

Oh! Thanks for pointing that out. Looking into it more, seems like I can also do RDF, which means I should be able to write datalog queries on it I think! https://www.zotero.org/support/dev/data_formats

teodorlu07:07:29

Any suggestions for an idiomatic solution to "splice" into a vector when it's not macro-time?

;; I'm trying to write a function that behaves like this:

(defn dl [_ & args] 'todo)
(dl "Static" "Normal document flow" "Relative" "...")
#_ => [:dl
       [:dt "Static"] [:dd "Normal document flow"]
       [:dt "Relative"] [:dd "..."]]

;; I figure I could use mapcat, but then I'd have to go via sequences. Perhaps
;; there's an idiom for this?

teodorlu07:07:26

I came up with this:

(defn dl [_ & args]
  (into [:dl]
        (mapcat (fn [[dt dd]]
                  (list
                   [:dt dt]
                   [:dd dd]))
                (partition 2 args))))
, but it seems a bit obscure. Comments welcome.

teodorlu07:07:34

I looked at the implementation if hash-map in clojure.core, but it just calls into Java.

phronmophobic08:07:29

I would probably do something like:

(defn dl [ & args]
  (into [:dl]
        (map-indexed (fn [i s]
                       [(if (even? i)
                          :dt
                          :dd) s]))
        args))

2
dgb2308:07:03

I agree with @U7RJTCH6J, if you’re set on using positional semantics then dispatching on even? is the thing you want

dgb2308:07:28

because that describes what is going on with your arguments

dgb2308:07:08

although I feel like your function is fine as well. It conveys what you’re doing more structuraly

teodorlu08:07:03

Though dispatching on even could leave a [:dt "definition term"] that doesn't have a corresponding [:dd "definition"]

dgb2308:07:14

but I dislike the signature, what it describes is a key value thing

dgb2308:07:24

why not pass a map?

teodorlu08:07:25

I need positional semantics. I'm making a <dl><dt>term</dt><dd>definition</dd>...</dl> HTML definition list. Otherwise I'd agree that a map would be a better representation 🙂

dgb2308:07:18

<dl> is a weird html element anyways. I dislike that it’s flattened and I almost always wrap the dt dd pairs into a div or something. for example if you style it later you very likely want them wrapped.

teodorlu08:07:24

> but I dislike the signature, what it describes is a key value thing Are you referring to the [_ & args] part? I don't like that either, it doesn't convey any intent.

dgb2308:07:07

pass a map and then map over it. you’ll get map entries that you can apply to the pairs

dgb2308:07:26

assuming dt is supposed to be unique

teodorlu08:07:56

This is where my use case gets unusual. I'm writing plain HTML in EDN files; experimenting with ways to create my own "components". Here's the EDN: https://github.com/teodorlu/subcons.teod.eu/blob/master/css-position/index.edn#L14 Here's the dl function: https://github.com/teodorlu/subcons.teod.eu/blob/master/src/teod/subcons/transform.clj#L14

teodorlu08:07:28

Thought I could use keywords for standard HTML and symbols for my own components.

dgb2308:07:19

[dl {"Static" "Normal Document Flow"
     "Relative" "??"
      ;;...
     }

dgb2308:07:00

I understand dl as a map, or that is when I typically use it

dgb2308:07:11

oh, it collides with the attribute map right?

dgb2308:07:03

put a nil in front then or sth, but generally I would say you pass a map into a dl component, or a list of entries. thinking about where the data comes from.

ChillPillzKillzBillz13:07:18

to run clojure code from clj... how do you define the entry point function? So far I've got clj -X script/<filename>.clj ... This is for clj only ... not Lein or boot.

noisesmith16:07:59

with -m you can specify a namespace, it expects a function called -main in that namespace

noisesmith16:07:34

or you can skip the -X and it will simply load the file you specify

noisesmith16:07:46

(assuming that file has some side effects on load)

ChillPillzKillzBillz18:07:29

so -main ... does it needs to be specified in the clj file or on deps.edn?

noisesmith19:07:13

the function needs to be called -main and take any number of string arguments, when clojure is provided with a namespace to execute it will call the function called -main with the args (and complain if the namespace doesn't have any function by that name)

noisesmith19:07:04

eg.

(defn -main
  [& args]
  (println "called with arguments" (pr-str args)))

👍 3
Pradeep B13:07:38

I am trying to parse this json using clojure/data.json — just want some help or confirmation on idiomatic way of doing it. Sample json input at pastebin • reading the json data using -> (thread first macro) , what if one of the key is missing (how to handle that gracefullly?) • to filter out all the users email whose role is non-admin? https://pastebin.com/ELB9WCkD

Pradeep B13:07:52

(filter get-non-admin-role-user (-> "/tmp/outjson.json"
    slurp
    json/read-json
    :data
    :users
    :output))

Pradeep B14:07:10

as roles is an array of map - that is causing some trouble if need to check for 2 different roles at same time.

Pradeep B07:07:41

Thanks @U8QBZBHGD for the detailed response. It is helpful. Yes, even i am also breaking my function (but little differently). ‘let’ destructring and ‘some’ looks neat.

dakra14:07:32

I'm just getting my feed wet with macros and fail with a very simple one.

(defmacro testmacro [xs]
  `(let [~(mapv symbol xs) ~xs]
     ~@(for [x xs]
         `(println ~(symbol x)))))
macroexpands to (let [[foo bar] [:foo :bar]] (println foo) (println bar)) and works as I want. but when I pass the macro a var like:
(let [input [:foo :bar]]
    (testmacro input))
I get an error: Don't know how to create ISeq from: clojure.lang.Symbol

danielneal14:07:02

Ah so, macros operate on the code itself, not on the runtime values. This macro will only work with the literal values [:foo :bar]. Otherwise it just sees input, which it can’t destructure - because it’s a symbol. Does that make sense?

dakra14:07:06

I think I kinda know what you're saying but it's still confusing 😄 How can I make it work that it works with [:foo :bar] as argument and with a variable.. I feel like I'm making some simple very basic mistake.

danielneal14:07:30

I don’t think you can. You would use a function instead.

danielneal14:07:38

What’s the bigger picture of what you’re trying to do?

danielneal14:07:11

think of macros as something that can look at the code and transform it into other code

danielneal14:07:18

you rarely need them for most things

dakra14:07:33

I use jackdaw for a kafka streams app. And I want a configurable amount of output topics but kafka streams (at least the version jackdaw is using) can't dynamically at runtime branch the input topic to multiple output topics. So I wanted to write a macro that reads the config and instead of the println in the testmacro it would be jackdaw/branch and jackdaw/to functions.

Ed17:07:27

@UFAP0C8KU Not sure if you got this sorted, but I don't think you need a macro to get jackdaw to send to multiple branches defined by some config. The jackdaw branch function takes a list of functions to decide how to branch and returns a list of streams. Doing something like this would let you dynamically branch but you'd need to do something with each of the returned streams. Like maybe send them all to a topic named in the config or something? (require '[jackdaw.streams :as k])

(defn branch-with [p]
  (fn [[_ {:keys [pet]}]]
    (= p pet)))

(let [config          {:branches ["dog" "cat" "fish"]}
      list-of-streams (k/branch source-stream (map branch-with (:branches config)))]
  list-of-streams)
Does that make sense?

dakra19:07:14

@U0P0TMEFJ thanks for the reply. All I want is to split the stream by some value like in your example and then output them to their own topic (in your example (k/to dog-stream (:topic-config dog)) etc) Somehow I thought a macro would be the easiest way. But now I think just use functions is probably better. I think I was confused because I had in my mind that I have to specify the k/to before I start the streams app, but now I realise I can just do that in my topology-builder function.

Ed20:07:09

Yeah. I think that's part of the design of the Kafka streams API, and why you build a topology. In general it's best to avoid macros as much as you can. Sometimes you need them, but once you're in macroland it's really hard to escape it again ;)

danielneal14:07:30

is the config known at compile time?

dakra14:07:58

No. It's a config I read in with aero at the start of the programm.

danielneal14:07:01

so as long as you know the config before you define the macro you can do something like this

danielneal14:07:05

(def config
  {:thing-1
   {:a 1 :b 2}
   :thing-2
   {:a 2 :b 3}})

(defmacro foo
  [k]
  (let [thing-config (get config k)
        {:keys [a b]} thing-config]
    (cond (= a 1)
          `[:= :a 1]
          (= a 2)
          `[:= :a 2])))

(foo :thing-2)
;; => [:= :a 2]

danielneal14:07:42

but if the system has to start up etc, I’m not sure how you’d do it. Probably is a way, but it might get messy.

dakra14:07:33

Thanks for the help. I think I understand it a bit more now. I'll play around some more and see if I can make it work (or find a way to make it work at runtime with a normal fn) 🙏

whatacold16:07:49

Hi, I'm wondering how to count characters in a string, just like some_str.count(char) in Python. And I come up with this:

(defn count-char
  "Count how many char in str"
  [str char]
  (reduce #(if (= char %2)
             (+ %1 1)
             %1) 0 str))
Am I doing this right?

whatacold16:07:06

What is the idiomatic way?

dpsutton16:07:42

(count "a string with characters") is what i would do. I have no idea how this handles more esoteric unicode points and emoji though. Not sure what your requirements are or if number of characters is even well-defined right now

dpsutton16:07:43

oh i totally misread that count. i'd probably do (get (frequencies your-string) \the-character)

dpsutton16:07:23

(get (frequencies "characters are contained") \c) would return 3 for the three cs in that string

dpsutton16:07:56

but frankly your solution seems 100% correct and readable

whatacold16:07:15

Yeah, the .count() in Python means to count the number a specific character in that string.

whatacold16:07:40

Your get version is very concise, I am going to look up its docs closely.

whatacold16:07:43

Look like (get map key) is for a map, why can it be applied to a string?

dpsutton16:07:32

it can (get "bob" 1) returns \o. It's just not helpful for you

dpsutton16:07:12

strings are indexed by position to character in an obvious fashion. And getting a character by index doesn't seem to help your case here

dpsutton16:07:01

wait, what do you mean "why can it be applied to a string"? I'm not using get on a string in the code i gave you

dpsutton16:07:40

(get (frequencies "characters are contained") \c) is called get on the map returned by (frequencies your-string). Check out (doc frequencies) to see what that returns.

Tomas Brejla19:07:54

you can also omit get as frequencies returns map, which can itself be used as a function. Also you might want to add a default value of 0 to be used instead of nil, otherwise you'd get nil for characters which are not present

user=> ((frequencies "characters are contained") \c 0)
3
user=> ((frequencies "characters are contained") \w 0)
0

Russell Mull16:07:48

Like many things in Clojure, get is defined over an abstraction. Anything that implements ILookup can be get -ed.

whatacold16:07:56

😂I get it now, just skipped frequences somehow :(

Jon Boone21:07:16

I’m new to clojure — and I’m wondering how frequently folks find the need to use more than one major.minor version of Clojure (0.9, 1.0, 1.1, etc.) for different projects. Do you find that most of your projects use the same version? If you have existing projects using legacy releases of Clojure, do you start new projects in a newer release? Or just tend to stick to one release across the board?

hiredman21:07:24

to my knowledge there never was a 0.9 of clojure

hiredman21:07:55

something that is somewhat different about the clojure ecosystem is the version of clojure used tends to be treated like just another library

hiredman21:07:24

so the clojure version is listed in your project dependencies along with the other libraries and their versions you use

Jon Boone21:07:03

That makes sense — it seems like lein, et al, take care of pulling the dependent version as if it were a library

hiredman21:07:21

because it is a library 🙂

dpsutton21:07:29

the versions are very stable going forward. i think there were a few things added in 1.9 alpha that ended up not in a release but i upgrade clojure versions when they come out and have a strong degree of confidence that nothing bad will happen

Jon Boone21:07:34

@dpsutton — does that “upgrade as releases are available” apply to both existing code bases and new projects? So you’re, essentially, always on the latest release?

hiredman21:07:43

if you are familiar with maven, clojure is just another jar in your local maven repo

hiredman21:07:09

I basically never upgrade my clojure version in projects unless forced

hiredman22:07:28

(that is personal projects, at work we often even deploy alpha clojure releases to production)

Jon Boone22:07:56

That’s very insightful feedback — thank you!

Jon Boone22:07:37

If you aren’t aggressively upgrading to new releases of clojure, is it fair to assume your other dependencies are the forcing function in most cases?

hiredman22:07:15

I avoid dependencies if at all possible largely for this reason (who has the time to keep up with all that)

hiredman22:07:34

I once had a dependency where I couldn't move to a newer version of clojure (there were some big changes around clojure 1.2, and the library was unmaintained)

hiredman22:07:58

I would say most clojure libraries are pretty tolerant in the version of clojure they work with

hiredman22:07:38

so even if the library says it wants clojure 1.10 it will likely work with 1.9

Michael Stokley22:07:49

i'm not very familiar with the zipper library. can i use it to carry arbitrary context with me as i traverse the tree? and use that context to inform the per-node replacement logic?

hiredman22:07:19

you can think of a zipper as a sort of iterator, a cursor over a tree, that you can navigate around in the tree, and do operations on to edit the tree, which is trivial for a mutable tree, but a zipper lets you do it on an immutable tree

Michael Stokley22:07:17

i see, thank you

Michael Stokley22:07:32

can i use the zipper library to build up context as i iterate from node to node, so that the changes i made in a previous node can inform future changes? or should i do that with an atom? or with a loop / reduce?

hiredman22:07:00

it depends what you mean

hiredman22:07:25

but basically yes, when you use a zipper you don't have access to the whole datastructure, you are basically building a recipe for rebuilding the tree as you descend into it, and rebuilding as you climb out of it

hiredman22:07:23

when I say a zipper is like an iterator, it very much is, when you use an iterator the looping and actual traversing is provided by you, and is not part of the iterator, the same is true of zippers

phronmophobic22:07:37

you may also be interested in something like https://github.com/redplanetlabs/specter

Michael Stokley22:07:58

our team is a bit skittish

Michael Stokley22:07:28

cautious, really. not skittish.

phronmophobic22:07:36

I think that's wise

phronmophobic22:07:34

but this is the problem that specter is aimed at solving and it does pretty well. with clojure.zip, I usually use zip/next rather than the left,right, up, down

hiredman22:07:36

I've never used specter or zippers in a real code base, I think because I always prefer to take trees, normalize them into some relational form, and do whatever I want there, and then re-generate the tree if needed

Michael Stokley22:07:30

zip/next instead of left right up down because you want to traverse the whole tree, not just certain nodes you know the exact locations of?

phronmophobic22:07:56

typically, it's because I'm working with a tree and I want to write a transformation recursively

phronmophobic22:07:16

eg.

(defn gen-table-of-contents [doc]
  (loop [zip (doc->zip doc)
         toc (make-toc)]
    (if (z/end? zip)
      (z/root toc)
      (let [node (z/node zip)]
        (if (instance? com.vladsch.flexmark.ast.Heading node)
          (recur (z/next zip) (add-section toc node))
          (recur (z/next zip) toc))))))

hiredman22:07:03

there is also clojure.walk

🙏 2
phronmophobic22:07:02

right, if it's a simple transformation, then clojure.walk can work, but zippers can be more flexible and you can collect information without side effects

🙏 2
Michael Stokley22:07:10

i've had good results with clojure.walk in the past. i gather the zipper lib is for more difficult...

hiredman22:07:02

you'd be surprised what you can achieve with clojure.walk smuggling data up and down attached as metadata

phronmophobic22:07:52

the other benefit of clojure.zip is that it can work on trees that have different shapes. Like you can make a zipper for hiccup data or even Java classes like in my gen-table-of-contents example

phronmophobic22:07:55

I've also used clojure.zip for editing ASTs where the branches are found in a specific place (eg. :children)

hiredman22:07:26

tools.analyzer has a custom tree walker with pre/post walks that does that

phronmophobic23:07:02

I was working with a non-clojure AST