Fork me on GitHub
#clojure
<
2022-10-06
>
ivana10:10:07

Hello. Is there any more elegant way for?

(->> [(foo) (bar)] (filter identity) first)

ivana10:10:51

I guessed that there are some Haskellers here, but it's Clojure 🙂

cjohansen10:10:48

(or (foo) (bar)) achieves the same

p-himik10:10:04

or is not exactly the same though. If (foo) is truth-ey, then (bar) won't be computed. But or is exactly the same if (bar) is free of side-effects.

ivana10:10:32

3 Haskellers already 🙂 or does not the same

ivana10:10:54

And yes, there are side effects

cjohansen10:10:14

I would not write side-effecty code like that

cjohansen10:10:52

Especially not if you expect side-effects from the first call, but only keep the result of the second

p-himik10:10:16

(some identity [(foo) (bar)])
But yeah, side effects in such a construct feel iffy.

👍 1
cjohansen10:10:57

I like to use doseq for sequences that have side-effects, communicates intention more clearly

👍 1
ivana10:10:59

Bot fns writes into different databases and I need the result of first but if not then second

cjohansen10:10:32

Then I would probably write it a little more elaborately so that is obvious

p-himik10:10:40

If you always have just 2 functions, I'd 100% write it as:

(let [f (foo)
      b (bar)]
  (or f b))

💯 6
ivana10:10:30

Doseq cant return result... What is your suggestion? I like above suggested (some identity [(foo) (bar)]) Extra let... Maybe...

p-himik10:10:05

FWIW, if it's just 2 functions, the some solution would never make it past my code review. :) The let solution would, immediately.

ivana10:10:01

Ok, thanks )

walterl12:10:26

> the some solution would never make it past my code review Interesting. Why is that, @U2FRKM4TW?

ivana12:10:12

I think the reason is more about conventions: let solution looks clear understandable & straightforward, but some solution provocates to rewrite it with or and break the code 😁 Although let keeps limitid & known amount of calculations only and you have to create names for bindings, and it't the second of 2 great programming problems (after cash invalidation) 😁

👍 1
p-himik12:10:02

@UJY23QLS1 Because the intent is just so much clearer. You need to do something, and then deal with the results. let is imperative and obvious, some has the potential to make you think in the wrong direction. Although, even with some it's possible to make it obvious if you name things using descriptive names. And while names can sometimes be hard to decide on, figuring out what a piece of code that needs some names but has none does is usually much harder.

👍 1
ivana13:10:46

Yep, regular practice for such descriptive names is add-smth! so excl mark shows the side effects 🙂

craftybones15:10:00

What about a doall ?

craftybones15:10:56

(doall [(foo) (bar)]) 
reads odd, but at least indicates the possibility of side effects

p-himik15:10:01

It's not only odd, it's completely unnecessary - there's a vector there already. ivana was right about ! in the names - it's a good indicator by itself.

roklenarcic12:10:27

I don’t have a windows machine to test this, but does this work?

(let [owner-read-permissions (PosixFilePermissions/asFileAttribute #{PosixFilePermission/OWNER_READ})
      tmp-file (Files/createTempFile "config" ".edn" (into-array FileAttribute [owner-read-permissions]))]
  tmp-file)

borkdude13:10:52

You could setup a Windows github action, to test this

borkdude13:10:03

When I'm back at home, I'd be happy to test

borkdude15:10:14

I can try now, just a moment

borkdude15:10:44

Not supported. Please use an example that I can just copy-paste in the REPL next time ;)

roklenarcic16:10:53

Thank you, does the first line fail where the file attribute is created or when tmp file is created?

borkdude16:10:41

If you will provide me something that I can paste in a REPL, to save me time, I will tell you the answer.

roklenarcic16:10:13

I am not at computer at the moment

roklenarcic16:10:59

Perhaps if you only post stacktrace of the error encountered earlier

borkdude16:10:27

user=> *e
#error {
 :cause "'posix:permissions' not supported as initial attribute"
 :via
 [{:type java.lang.UnsupportedOperationException
   :message "'posix:permissions' not supported as initial attribute"
   :at [sun.nio.fs.WindowsSecurityDescriptor fromAttribute "WindowsSecurityDescriptor.java" 358]}]
 :trace
 [[sun.nio.fs.WindowsSecurityDescriptor fromAttribute "WindowsSecurityDescriptor.java" 358]
  [sun.nio.fs.WindowsFileSystemProvider newByteChannel "WindowsFileSystemProvider.java" 227]
  [java.nio.file.Files newByteChannel "Files.java" 370]
  [java.nio.file.Files createFile "Files.java" 647]
  [java.nio.file.TempFileHelper create "TempFileHelper.java" 137]
  [java.nio.file.TempFileHelper createTempFile "TempFileHelper.java" 160]
  [java.nio.file.Files createTempFile "Files.java" 912]
  [user$eval152 invokeStatic "NO_SOURCE_FILE" 2]
  [user$eval152 invoke "NO_SOURCE_FILE" 1]
  [clojure.lang.Compiler eval "Compiler.java" 7181]
  [clojure.lang.Compiler eval "Compiler.java" 7136]
  [clojure.core$eval invokeStatic "core.clj" 3202]
  [clojure.core$eval invoke "core.clj" 3198]

roklenarcic16:10:05

Yeah create temp file failed. This helps a lot.

👍 1
Nazral15:10:15

Hi! Just a quick question, is Clojure-CLR ok to use in a production setting? I saw that there is a rewrite in the works, but haven't found much besides that

Drew Verlee18:10:48

My current understanding, based on this https://lambdaisland.com/blog/2020-06-12-logging-in-clojure-making-sense-of-the-mess, is that i should, in order for logback and slfj to reliable work, exclude all other logging library's. e.g

commons-logging
    log4j
    org.apache.logging.log4j/log4j
    org.slf4j/simple
    org.slf4j/slf4j-jcl
    org.slf4j/slf4j-nop
    org.slf4j/slf4j-log4j12
    org.slf4j/slf4j-log4j13
the post suggests inspecting the deps tree to find them. Is this what everyone is doing? Every time you add a lib you check it's deps to see if it contains one of an unknown and growing number of logging libs to exclude them so the logging setup works? Is there a way to say what i want to do, not all the things i don't want to do? Or has that ship sailed somehow?

seancorfield18:10:25

I think that will break things that expect a specific logging library. What you need are the bridge libraries that wire those other libs to route through slf4j/logback.

seancorfield18:10:01

(which is what that article is saying you should do)

seancorfield19:10:21

@U07FP7QJ0 Can you clarify, as the author of that?

seancorfield19:10:01

At work, we certainly do not exclude any logging libraries. We specify -- as top-level deps -- log4j2 and the various bridge libraries that route all other logging to log4j2, and that works as expected: everything from all libraries we use flows through log4j2 and we can control it all from our log4j2 .properties file.

Ivar Refsdal19:10:26

A well written library won't pull in e.g. org.slf4j/simple or slf4j-nop, only the slf4j-api. Thus you only need to start excluding stuff if slf4j complains about multiple bindings present. I don't recall having to exclude any logging bindings recently. (Datomic used to include slf4j-nop, but they removed it). We are using timbre + slf4j-timbre and are pretty happy with it.

seancorfield19:10:41

Timbre is its own special kind of hell IMO -- we used it for a while but were very happy to get off it. But, some people love it 😐

Ivar Refsdal19:10:37

Strong words. What's wrong with Timbre?

Drew Verlee19:10:01

As an example of library i assume i need to exclude, clj-http brings in commons-logging. From the deps tree:

clj-http/clj-http 3.12.3                         
    . commons-logging/commons-logging 1.2 
  

Ivar Refsdal19:10:02

Don't assume you need to exclude anything... I've used clj-http a lot and never excluded anything IIRC

seancorfield19:10:12

Right, no need to exclude that in my experience. No need to exclude any of them.

Drew Verlee19:10:41

ok, thanks a ton. The issue with my project is likely something else. What i know is that running the jar with the log4j selection correctly pushes the msgs to system out:

java -Dclojure.tools.logging.factory=clojure.tools.logging.impl/log4j-factory -jar app/foo-web.jar
However using slf4j doesn't
java -Dclojure.tools.logging.factory=clojure.tools.logging.impl/slf4j-factory -jar app/foo-web.jar
But the app is configured to use slf4j. e.g we have a logback.xml file and logback-classic along with clojure tools logging in the deps e.g
org.slf4j/slf4j-api {:mvn/version "1.7.30"}
        org.slf4j/jul-to-slf4j {:mvn/version "1.7.30"}
        org.slf4j/jcl-over-slf4j {:mvn/version "1.7.30"}
        org.slf4j/log4j-over-slf4j {:mvn/version "1.7.30"}
        org.slf4j/osgi-over-slf4j {:mvn/version "1.7.30"}

        org.clojure/tools.logging {:mvn/version "1.2.4"}
        ch.qos.logback/logback-classic {:mvn/version "1.2.3"} ;; 1.2.3
I figured i needed to exlucde things because somehow things are getting correctly sent via log4j but no slfj4.

seancorfield19:10:09

Are you getting any warnings from slf4j at startup? (about multiple bindings or about no-op logging)

👀 1
Drew Verlee19:10:26

Yes. multiple bindings and there is mention of a "sljfj-nop-1.7.22" not sure that s a no-op logging though. full output:

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/home/drewverlee/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/home/drewverlee/.m2/repository/org/slf4j/slf4j-nop/1.7.22/slf4j-nop-1.7.22.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See  for an explanation.
14:15:18,688 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
14:15:18,689 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
14:15:18,689 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback.xml] at [file:/home/drewverlee/Centriq/centriq-web/backend/app/classes/logback.xml]
14:15:18,690 |-WARN in ch.qos.logback.classic.LoggerContext[default] - Resource [logback.xml] occurs multiple times on the classpath.
14:15:18,690 |-WARN in ch.qos.logback.classic.LoggerContext[default] - Resource [logback.xml] occurs at [file:/home/drewverlee/Centriq/centriq-web/backend/app/classes/logback.xml]
14:15:18,690 |-WARN in ch.qos.logback.classic.LoggerContext[default] - Resource [logback.xml] occurs at [file:/home/drewverlee/Centriq/centriq-web/backend/resources/logback.xml]
14:15:18,761 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - debug attribute not set
14:15:18,762 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
14:15:18,768 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT]
14:15:18,772 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
14:15:18,813 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to DEBUG
14:15:18,813 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[ROOT]
14:15:18,814 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration.
14:15:18,814 |-INFO in ch.qos.logback.classic.joran.JoranConfigurator@7f65639e - Registering current configuration as safe fallback point

SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]

seancorfield19:10:22

Yeah, that's the no-op lib. You should exclude that because it is suppressing slf4j output. What is bringing that in, in your dependencies?

seancorfield19:10:22

(a library absolutely should not bring in the no-op logger -- that's really kind of nasty behavior!)

Drew Verlee19:10:32

probably something i included... in the deps

org.slf4j/slf4j-api {:mvn/version "1.7.30"}
        org.slf4j/jul-to-slf4j {:mvn/version "1.7.30"}
        org.slf4j/jcl-over-slf4j {:mvn/version "1.7.30"}
        org.slf4j/log4j-over-slf4j {:mvn/version "1.7.30"} <--- like this
        org.slf4j/osgi-over-slf4j {:mvn/version "1.7.30"}

        org.clojure/tools.logging {:mvn/version "1.2.4"}
        ch.qos.logback/logback-classic {:mvn/version "1.2.3"} ;; 1.2.3

seancorfield19:10:05

Use clojure -Sdeps tree to see what's actually bringing it in.

seancorfield19:10:32

None of those slf4j deps would bring in the no-op lib.

Drew Verlee19:10:38

does this mean it's datomic pro bringing it in?

com.datomic/datomic-pro 0.9.5966
  . org.clojure/tools.cli 0.3.5 :newer-version
  . org.fressian/fressian 0.6.5
  X org.slf4j/jul-to-slf4j 1.7.22 :older-version
  . org.slf4j/slf4j-nop 1.7.22 <----------------------------------------

Drew Verlee19:10:07

ill ask on the datomic forum...

seancorfield19:10:53

Yup, that's Datomic pulling it in.

seancorfield19:10:11

I bet there's an updated version that doesn't do that (or at least I hope there is!).

Ivar Refsdal19:10:58

yeah there is an updated version 👍

Ivar Refsdal19:10:18

1.0.6397 is the newest (and no slf4j-nop)

seancorfield19:10:34

☝️:skin-tone-2:

Drew Verlee19:10:19

i'll probably just update datomic pro then. Thanks @U04V70XH6 and @UGJE0MM0W i really do appreciate the help. Let me know if i can do something in return.

plexus01:10:33

Sorry for causing confusing. The advice given here is what I would say as well.

1
plexus01:10:01

I also second what @U04V70XH6 says about timbre. It tries too hard to reinvent the wheel.

1
seancorfield02:10:17

@U07FP7QJ0 So what is the comment about excluding libraries/dependencies meant to suggest in the article?

plexus16:10:28

It's fairly explicit

plexus16:10:27

The main thing to watch for is to exclude the nop backend, but generally you want to have a single path from any logging API to the logging backend, and not have logs piped to other logging libraries you're not using.

👍 1
Drew Verlee16:10:08

@U07FP7QJ0 the advise in the post was very useful, but as a new comer it was hard to understand how to prioritize debugging why my logs were being suppressed. Also I misunderstood this bit, maybe i still do: "a lot of libraries will indirectly pull in the "nop" backend which is simply a placeholder that doesn't log anything." What i know understand is that ""a lot of libraries will indirectly pull in a "nop" backend which might cause your logging framework to not log at all, it makes them a no operation". The also posts suggests i exclude from a list of libs, but in my case only the org.slf4j/slfj4-nop library mattered in that it actually undid the work setup else where. Two globally fighting settings as it were. Others here are indicate that they never exclude the other kinds (e.g commons-logging), so it seems on average thats not a high priority item for most. Finally, as i pointed out in the #lambdaisland chat the regex to find libs to exlude didn't work as i expected and so I falsey believed i had nothing i needed to exclude for a bit. Your post is the beacon of light in the darkness of clojure logging, let me know if i can help with it in any way. Hopefully i'll have time to getting around to writing a post about what i learned.

Colin P. Hill19:10:06

Is there any way to atomically alter both the root binding and the metadata of a var?

hiredman20:10:36

no, but that sounds like you mutating vars concurrently as part of "normal" application code, instead of mutating them as part of code loading / redefinition at the repl, which is not how vars are usually used

Colin P. Hill20:10:53

It's more that I'm writing an instrument-like function and wish to program defensively if there's a straightforward way to do so, in case it's used in a context I don't intend.

hiredman20:10:34

instrument uses a lock (which will protect against multiple instrument calls, but the lock is instrument only, so won't protect against being concurrently run with something else that modifies vars)

👍 1
Colin P. Hill20:10:52

Ah, I overlooked that lock. Yeah, I suppose that strategy will work for me. I wasn't originally planning on a central registry of changed vars (since I don't really have a need for an equivalent to unstrument), but that's a good way to get idempotence, which is the most important safeguard I'm after. It's realistic in this case to imagine that the function will be adopted in a relatively disorganized way, and might be called more than once.

respatialized21:10:27

Is there a version of time that returns the duration as a value?

pppaul16:10:33

i copy/paste the time macro and change it for different cases that i'm interested in (like adding a label to the output, or something like what you requested). there is also https://github.com/ptaoussanis/tufte if you want more sophisticated profiling

Alex Miller (Clojure team)21:10:58

no, but how hard could it be? :)

kenny21:10:43

Blindly keywordizing keys from external data has bitten many of us. Does anyone know of a written post somewhere talking about the perils?

2
Alex Miller (Clojure team)21:10:56

this is related but not exactly what you're asking for: https://clojure.org/guides/faq#unreadable_keywords

1
devn00:10:37

yeah I was going to say I’m not aware of one, but a couple of “look at these oopsies” might be enough to make the point, and the link Alex posted is most of the way there. In addition, at some point I believe Zach Tellman showed some info on constantly keywordizing keys in web requests for the sake of calling keywords as functions instead of using get with strings. The time it takes to keywordize keys for apps that need to be high performance is definitely not nothing, so it’s another reason imho to at least give a bit of thought to whether that’s an appropriate thing to do at the boundaries of a service.

devn00:10:26

Most apps I’ve worked on /do/ keywordize keys, but if it is a source of data you don’t have much control over, or if you are going to need to be fielding a large number of requests and millis matter, it’s worth thinking about.

delaguardo08:10:47

There is also a theoretical security vulnerability related to automatic keywordisation of keys 🙂 Once I highjack development environment of my colleague forcing him to make an attempt to debug the failed request sent to API. The payload was constructed to produce 500 response and I know someone will try to debug using logged request. The problem is that request contains a key that looks like this: "foo (start-nrepl-server) foo" So when my colleague blindly copy paste such request to the repl it gets connected to my system and I was able to sent arbitrary code to execute. Obviously such vulnerability is quite hard to exploit as a generic attacking vector but it is possible 🙂

👀 1
pppaul16:10:36

keywordizing keys is redundant much of the time too thanks to {:strs [keyname]}

👍 1
kenny16:10:35

One way that I can recall it has bitten me is from a tooling perspective. My REPL won't correctly syntax highly an edn structure with an invalid keyword (e.g., one with spaces), making debugging difficult.

James Amberger13:10:45

@U0LAJQLQ1 can you expand on that?

pppaul14:10:58

@U032EFN0T33 you can destructure symbols, string, and keyword keys.

1