Fork me on GitHub
#clojure
<
2022-02-18
>
simongray10:02:59

I need the behaviour of when-let in my cond. Do I go with cond-let or better-cond? https://github.com/walmartlabs/cond-let https://github.com/Engelberg/better-cond

2️⃣ 1
Ben Sless10:02:26

What are the chances a pattern matching macro will be more readable in this case?

eskos14:02:51

I’ve been pretty happy with https://github.com/clojure/core.match maybe this is what you’ll need?

simongray16:02:00

No, I don’t think pattern matching is a good fit here. The input is all strings. I want let behaviour since I need to destructure results from re-matches in some cases, but I want to do so on demand. For now I have gone with better-cond after @U04V15CAJ suggested it.

1
Joshua Suskalo17:02:27

Not sure just how related this is, but I made a macro ages and ages ago I just put into a gist that may be connected: https://gist.github.com/IGJoshua/09c0e4fcd7e496a611bc8724fce96e0c

Joshua Suskalo17:02:55

It allows you to make a cond expression to match some expression against a list of regexes in order, while creating local bindings for the capture groups.

Ben Sless20:02:40

The regex operator in meander does that

Joshua Suskalo20:02:19

oh cool, I didn't know about that library.

Joshua Suskalo20:02:28

Oh hmm, it might not have existed when I wrote this macro

Joshua Suskalo20:02:49

I wrote this for a discord bot I wrote back in late 2017

Ben Sless21:02:41

meander is pretty cool 🙂

hanDerPeder12:02:57

What do people use to view structured edn logs? Looking for something bigger than a babashka script and smaller than ELK

Linus Ericsson12:02:03

maybe the unix tool bat (a cat with syntax highlighting etc) would work? https://github.com/sharkdp/bat/releases

msolli13:02:34

I use jq for my JSON logs:

aws logs tail /some-log-group --follow | cut -f 3- -d ' ' | jq -R '. as $line | try (fromjson) catch $line'
For EDN, I’d use jet (https://github.com/borkdude/jet) with the --pretty option.

emccue13:02:03

do I need to switch off of cognitect test-runner to get code coverage?

vemv23:02:40

over the years I've learned to decouple test coverage from test suite runner, they will be different steps in CI (perhaps performed in parallel) else you'll find Cloverage intricacies dictate how your test suite runs. It's worth emphasizing that cloverage instruments that code, to date that isn't issue-free, so there's value in running your tests in a raw/uninstrumented way

lilactown15:02:46

what's the state of the art in markdown to AST parsers for Clojure?

lilactown15:02:17

this didn't show up in my google searches, but I recalled there had been something new like this released recently! ty

Joshua Suskalo16:02:49

np! @U012RPAAR0E is occasionally around on slack if you have questions too.

lilactown16:02:28

what's the motivation & tradeoffs you're considering? i.e. why not use cybermonday?

mkvlr16:02:38

besides not being aware of cybermonday our lib builds on markdown-it and uses graal on the JVM to deliver consistency across runtimes

mkvlr16:02:58

goals are similar in getting data out including ToC and handing a bunch of extensions. Because we build on markdown-it we could integrate with our nextjournal editor built on prosemirror eventually https://prosemirror.net/examples/markdown/

lilactown16:02:08

if I wanted to use this in the JVM, I'd need to use Graal so I could load markdown-it (which is a JS lib)?

lilactown16:02:44

maybe I could do the same thing for org mode. a lot of the Java and Clojure org parsers seem pretty low quality

lilactown16:02:10

it's an insane task so I don't consider it a slight against any of the authors 😄

mkvlr16:02:14

yep, that was our thinking and why we built on the state of the art js impl.

mkvlr16:02:18

if the browser isn't a target, pandoc is also worth checking out

mkvlr16:02:32

enjoyed this podcast on the subject recently https://overcast.fm/+Y-HXW34kI

👍 1
lilactown17:02:36

I think pandoc is really what I want. that's a great suggestion

lilactown17:02:51

just have to figure out how to integrate it so I can get an AST

lilactown17:02:59

this is certainly turning into a 🐰🕳️

kiranshila17:02:33

@U5H74UNSF interesting using markdown-it and nashorn for jvm consistency. It took me quite a bit of time to get an IR that matches flexmark and remark

kiranshila17:02:18

I found remark’s ast much nicer than markdown-it’s fwiw

mkvlr17:02:56

we went with markdown-it because that’s what prosemirror settled on

mkvlr17:02:58

and are currently looking at making io.github.nextjournal/markdown compatible with https://pandoc.org/MANUAL.html#pandocs-markdown

dvingo17:02:02

there is also this implementation used by logseq - not technically a clj impl though https://github.com/logseq/mldoc seems to be used from cljs here: https://github.com/logseq/logseq/blob/e0888b896f525465b99dff9c28806d8001fc2d37/src/main/frontend/format/mldoc.cljs

Joshua Suskalo17:02:11

Glad to have the mention of #coffi 😉

❤️ 1
kiranshila17:02:33

@U4YGF4NGM in cybermonday, you can get an ast with the md-to-ir fn in the ir namespace, if you wanted to give it a try

1
lilactown17:02:21

yeah that's really nice. if I give up on pandoc I'll probably try cybermonday next

👍 1
lilactown17:02:44

I really want to support more than just md - md is a good starting point tho

kiranshila17:02:05

Yeah Pandoc works with org too, iirc. @U4U7PHRL7 went down this rabbit hole a bit at one point

phronmophobic17:02:35

I've been using https://github.com/vsch/flexmark-java (same parser cybermonday uses) directly and it's worked well for me.

lilactown22:02:46

@U5H74UNSF that podcast is great!

lilactown22:02:37

i guess the best way to use pandoc is to shell out? there are some wrapper APIs but they're out of date and/or I think they simply shell out regardless

paulbutcher15:02:33

I’ve just started playing around with tools.build. I was expecting that compile-clj would pick up the :paths and :extra-paths within my deps.edn (for the aliases passed to create-basis). But that doesn’t seem to be the case? Am I missing something? Is there a reason why :src-dirs need to be passed independently of what’s already in deps.edn?

Alex Miller (Clojure team)15:02:49

this was just asked this morning in #tools-build :)

Alex Miller (Clojure team)15:02:17

there are cases where your paths are not exactly what you want to compile but I think it would be a reasonable default and I'll probably look at it later today

paulbutcher15:02:47

Great - thanks!

Søren Sjørup17:02:43

Why is it we have

(= 1M 1N) ; => false
(== 1M 1N) ; => true
in Clojure?

Ferdinand Beyer17:02:18

Not 100% sure. From the behavior of = I would say it is designed to differentiate between integers, decimals/fractions and floating point numbers:

(= 1 1/1) ; => true
(= 1N 1). ; => true
(= 1 1.0) ; => false
If you don’t care about this distinction, you can use == to compare all types of numbers.

andy.fingerhut20:02:29

I think one of the reasons that = returns false is that it is probably extremely difficult to implement hash to be consistent with = if = behaved like == for numbers.

andy.fingerhut20:02:29

This article I wrote mentions that deep in the middle somewhere, but the article is mostly about the "what" of = and ==behavior, because the "why" answers I can only really make educated guesses about.

dpsutton17:02:45

check out (doc =) and (doc ==)

💪 1
Ferdinand Beyer17:02:40

Returns non-nil if nums all have the equivalent
value (type-independent), otherwise false
Actually a good example where we could have clearer docstrings. Why “non-nil” instead of “true”? :D

Søren Sjørup17:02:20

Thank you. Not sure I understand why (= 1 1N) ; => true

Ferdinand Beyer17:02:54

The N suffix is for Java’s BigInteger. So it is an integer type, and can be safely compared with other “precise” number types. Numbers suffixed with M are Java’s BigDecimals and therefore not as precise as integers. Doing math with decimals could involve rounding and therefore introduce errors. Therefore = differentiates between these “classes” of numbers.

dpsutton17:02:52

to compare numbers it ensures the “categories” are the same. And that lookup is here:

static Category category(Object x){
	Class xc = x.getClass();

	if(xc == Integer.class)
		return Category.INTEGER;
	else if(xc == Double.class)
		return Category.FLOATING;
	else if(xc == Long.class)
		return Category.INTEGER;
	else if(xc == Float.class)
		return Category.FLOATING;
	else if(xc == BigInt.class)
		return Category.INTEGER;
	else if(xc == Ratio.class)
		return Category.RATIO;
	else if(xc == BigDecimal.class)
		return Category.DECIMAL;
	else
		return Category.INTEGER;
}

dpsutton17:02:58

(i had never seen this stuff so interesting to me)

Søren Sjørup17:02:23

Thank you both!

Aneil Mallavarapu17:02:42

Hi - Is there any way to replace the custom gensym function used by clojure.core macros? (I'm trying to get macroexpansion to produce identical code given the same input). I know I can (alter-var-root #'clojure.core/gensym my-custom-gensym) but when I attempt to macroexpand clojure.core macros. E.g., (macroexpand '(doseq [a [1 2 3]] (println a))), it ignores my custom gensym function. I assume clojure.core has optimized away the var derefrencing. Is there anyway to work around that?

ghadi17:02:45

what is the high level goal in producing identical code from the same input?

Aneil Mallavarapu18:02:44

The answer is rather complicated. But two things need to be true: variables inside expanded code bodies need to be stable because they are captured in persistent storage. Also, I need to produce stable hashes from the AST. So gensyms ruin that. Common lisp allows you to dynamically bind *gensym-counter* , but that counter is in a private Java field in Clojure. I even tried setting the private id counter in clojure.lang.RT. which is used by autogensym, but that does not seem to affect gensym .

Aneil Mallavarapu18:02:22

It does not seem to affect gensyms generate in clojure.core macros, I mean. Doing alter-var-root on clojure.core/gensym or replacing the id field in clojure.lang.RT, using for example org.apache.commons.lang3.reflect.FieldUtils has no effect.

ghadi18:02:54

you are correct that core functions are linked in a different way that cannot be overridden

Joshua Suskalo18:02:33

you could write a codewalker that looks for gensyms and strips the number off of them. It wouldn't work in cases where the macros genuinely want to have a number as the end of a local, non-auto-gensymmed symbol, but those cases would be rare.

ghadi18:02:50

np, are you doing some content-based hashing? @U01G423GNFP or a database of forms?

Aneil Mallavarapu18:02:19

@U5NCUG8NR, yes I think I'll have to do that. I guess I can rely on the guarantee that gensym vars always end in numbers.

Aneil Mallavarapu18:02:17

@U050ECB92 - I'm capturing execution state of functions and need the variables to be stable.

Joshua Suskalo18:02:56

trying to do continuations?

Joshua Suskalo18:02:27

good luck :thumbsup:

ghadi18:02:41

java is getting virtual threads Soon™

Joshua Suskalo18:02:58

You could look at core.async for a CPS transform that it does

👍 1
Joshua Suskalo18:02:45

but the reality of current continuation libraries (and maybe just a reality of libraries that don't try to modify bytecode on the fly?) is that they only work by CPS transforms, so you can't just save a continuation in any function, only in the ones that go through this transform.

Joshua Suskalo18:02:13

I'm looking forward to project loom though, and I'm hoping they expose the internal continuations used for that.

Joshua Suskalo18:02:18

although I doubt they will

ghadi18:02:28

I hope they don't.

Joshua Suskalo18:02:33

I guess I hope they do in an entirely academic sense. I have no desire to write code for production that uses continuations, and I have no desire for the issues in libraries that exit continuations bring as evidenced by scheme.

borkdude18:02:35

@U01G423GNFP If you need a reader for fn literals that produces stable results, I have one.

borkdude18:02:18

Not sure if your problem is beyond that.

borkdude18:02:42

user=> (require '[edamame.core :as e])
nil
user=> (e/parse-string "#(foo % %2)" {:all true})
(fn* [%1 %2] (foo %1 %2))
user=> (e/parse-string "#(foo % %2)" {:all true})
(fn* [%1 %2] (foo %1 %2))

👀 1
emccue19:02:11

Even if they don't expose the continuations, for all the "fun" stuff we can just do --add-opens

clojure-spin 1
🙈 1
emccue19:02:37

I don't think anyone using a custom continuation library would flinch at that

Joshua Suskalo19:02:36

yeah that's fair

Aneil Mallavarapu14:02:42

@U04V15CAJ - hey, thanks, that's kind of you. I think I solved it using @U5NCUG8NR's suggestion. I'm just doing a tree walk. (It wasn't so bad after all.)

borkdude14:02:39

in case you need it next time, it's here: https://github.com/borkdude/edamame

joshkh22:02:57

clojure.repl/dir is a macro and i'm rusty on my unquoting and splicing. how can i pass it a parameter from a function?

(defn functions
  [namespace]
  (clojure.repl/dir namespace))

(functions 'my.core.ns)
Execution error at my.core.ns/functions (instrument.clj:7).
No namespace: namespace found 

1
phronmophobic22:02:54

try using clojure.repl/dir-fn instead

kbaba100123:02:42

I noticed that clojure.string/replace can accept number, for example:

 (clojure.string/replace 123.456 #"\." "a")
 ;; "123a456"
but, the same code throw an error in CLJS:
 (clojure.string/replace 123.456 #"\." "a")
 ;; Execution error (TypeError) at (<cljs repl>:1).
 ;; s.replace is not a function
Is this a bug? I read a code of this https://github.com/clojure/clojure/blob/master/src/clj/clojure/string.clj#L101 . The replace function use .toString in the first for char/pattern, but this can accept number too.

p-himik23:02:19

Not a bug. The CLJ version uses .toString internally because its argument might be any subclass of CharSequence which is not a string itself. By passing a number there, you're using it wrong. The fact that it works is accidental.

kbaba100123:02:00

I understand this usage is wrong, but should this function throw an exception?

p-himik23:02:23

Such an approach would require checking every single argument of every single function - IIRC there has been an explicit decision at some point to not do that. But there are optional function specs. No clue whether the built in ones include that particular check though.

1
Joshua Suskalo15:02:57

Yes, this is the result of 'garbage in, garbage out'. Passing a number is basically the equivalent of undefined behavior in C++. It happens to do something reasonable-ish, but it's not intended.