clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
mpenet 2020-12-16T06:14:24.071300Z

Yes you can teach it not to try to coerce the value, by returning :exoscale.coax/invalid, in that case it will just leave the value passed untouched and you can let it blow up when you validate. In general it is good to add some checks on what the expected input is in the coercer fn, like cond string? etc etc... (about invalid, it's useful to differentiate know about impossible coercion internally sometimes, like with s/or. coax tries hard to never do any s/valid? call to do coercion, it does just one thing and with as little "code dependency" as possible, so we must have this possible return value on coercers otherwise I cannot differentiate between a coercer that just returns the input untouched because it's wrong or because it was acceptable in current form).

mpenet 2020-12-16T06:14:51.071500Z

You might want to check the default coercers for examples https://github.com/exoscale/coax/blob/master/src/exoscale/coax/coercer.cljc

mpenet 2020-12-16T06:19:54.071900Z

Also on the repl be aware of coercers caching (we cache the coercer fn returned for a spec form/key, so you pay for spec parsing only once per spec) , you ll have to turn it off not to have surprises https://github.com/exoscale/coax#caching

mpenet 2020-12-16T06:20:43.072100Z

I should put a big bold warning about this on the readme

msolli 2020-12-16T06:57:39.076500Z

I have a weird error in production sometimes. It’s an uberjar by Leiningen, and it’s a mix of .clj and .cljc files. Sometimes (but only sometimes!) after a deploy, the new instance will start reporting clojure.lang.Compiler$CompilerExceptions for a few forms in .cljc files. These are files that have been built and tested locally and on CI without error, and runs fine on the other instance. In other words, a moon-phase bug. 🙂 Restarting the app server (with the same code) always solves the problem. I don’t know how to even start to form a theory of what’s going on here. Here’s a sample excerpt of a stack trace (with names pseudonymized):

...
                                                                    clojure.core/require                       core.clj: 6007 (repeats 2 times)
                                                                      clojure.core/apply                       core.clj:  667
                                                                                     ...
                                                                  clojure.core/load-libs                       core.clj: 5969
                                                                  clojure.core/load-libs                       core.clj: 5985
                                                                      clojure.core/apply                       core.clj:  667
                                                                                     ...
                                                                   clojure.core/load-lib                       core.clj: 5928
                                                                   clojure.core/load-lib                       core.clj: 5947
                                                                clojure.core/load-lib/fn                       core.clj: 5948
                                                                   clojure.core/load-one                       core.clj: 5908
                                                                                     ...
                                                                       clojure.core/load                       core.clj: 6109
                                                                       clojure.core/load                       core.clj: 6125
                                                                    clojure.core/load/fn                       core.clj: 6126
                                                                                     ...
                                                                        foo.bar/eval3949                       bar.cljc:    1
                                                        foo.bar/eval3949/loading--auto--                       bar.cljc:    1
                                                                                     ...
                                                                    clojure.core/require                       core.clj: 6007 (repeats 2 times)
                                                                      clojure.core/apply                       core.clj:  667
                                                                                     ...
                                                                  clojure.core/load-libs                       core.clj: 5969
                                                                  clojure.core/load-libs                       core.clj: 5985
                                                                      clojure.core/apply                       core.clj:  667
                    java.lang.Exception: namespace 'foo.xyzzy' not found
2020-12-15T17:38:09.900000+00:00 i-002af1a1b62baa6d3 clojure.lang.Compiler$CompilerException: Syntax error compiling at (foo/bar.cljc:1:1).
    data: {:clojure.error/phase :compile-syntax-check,
           :clojure.error/line 1,
           :clojure.error/column 1,
           :clojure.error/source
           "foo/bar.cljc"}

Renato Alencar 2020-12-16T11:39:13.092900Z

It looks like something that it's not being built or no being found. I would inspected the jar in order to see if the class generated for the namespace is there, it should be something like bar__init.class, if this is not being found it tries to compile it's local clj files on the class path.

2020-12-16T07:12:41.078Z

the first thing I'd look for is def calls with code that might throw exceptions

2020-12-16T07:13:03.078400Z

in the namespace that isn't found

2020-12-16T07:13:20.078700Z

also, be sure you don't have any code that uses a namespace without requiring it

valerauko 2020-12-16T07:19:04.079100Z

I had no idea there were so many

msolli 2020-12-16T08:12:45.080900Z

Thanks, @noisesmith! Yeah, I’ve checked those things. No side-effecting defs, and all namespaces are required before use.

msolli 2020-12-16T08:41:11.085600Z

I think it might be a concurrency issue. I noticed it happens when two web requests are processing concurrently on the same instance, just after restart. There is a dynamic require for a view (the .cljc file) in there. I don’t know much about the inner workings of require, but might it be possible that some Vars that are being (re-)defined in one thread are “invisible” to other threads? The first CompilerException I get after a new instance launches is always with an error like this in the dynamic require call: “java.lang.RuntimeException: No such var: xyzzy/some-var”

kwladyka 2020-12-16T09:16:23.088500Z

tree src/api/logs
src/api/logs
├── CustomLayout.java
├── CustomMessage.java
└── core.clj
Any simple way to use .java with deps.edn and REPL and make .java files automatically reload / compile ? How do you deal with java files when coding in Clojure? Do you really make separate project for 2 files? I would like to keep Clojure experience in REPL even when add this 2 java files.

p-himik 2020-12-16T09:17:43.088600Z

https://github.com/ztellman/virgil

p-himik 2020-12-16T09:19:26.088900Z

Although it's designed to work with Leiningen and Boot, you can still use it in any Clojure project - you just have to write some code for it. In my case, most of that code was just copied from virgil itself and then changed a bit.

kwladyka 2020-12-16T09:22:24.089100Z

thank you

kwladyka 2020-12-16T09:23:03.089300Z

But it is strange there is no tool for that already. Maybe separate project is the right call then hmm.

kwladyka 2020-12-16T09:23:38.089600Z

What about all dependencies and versions then between 2 files java project and whole deps.edn project?

p-himik 2020-12-16T09:26:22.089800Z

It will become conceptually simpler but harder to implement, support, and use. And the hot reloading capability will be gone.

kwladyka 2020-12-16T09:27:15.090Z

on the other hand I don’t want to maintain my own personal virgil 🙂 It looks I will lose whatever path I will choose 🙂

p-himik 2020-12-16T09:30:15.090300Z

It's not "your personal virgil" - I just copied 30 lines from it. And even those were because I'm using a newer JVM that's somehow incompatible with the way virgil does things (I had to change literally one line) and I wanted to provide an explicit set of Java files (virgil searches for Java files itself).

p-himik 2020-12-16T09:30:40.090500Z

BTW "I don’t want to maintain my own personal virgil" is a great answer for why "there is no tool for that already" ;)

Dennis Tel 2020-12-16T10:02:02.091200Z

Hi, I’m trying to call Clojure from Java/Kotlin and a function requires a map. Is there a list/place where the interfaces for these objects are defined?

p-himik 2020-12-16T10:12:30.091300Z

FWIW I just use Clojure's source code as such a place.

Dennis Tel 2020-12-16T10:19:15.091500Z

Do you by any chance know if you can directly use the Class clojure itself uses? or is it common to define your own implementation to the interface?

p-himik 2020-12-16T10:30:27.091700Z

I have seen both. Depends on what you need. If you just need a Clojure map then reusing the existing classes makes sense. If you need custom behavior then a custom implementation makes sense.

valerauko 2020-12-16T10:30:28.091900Z

You can, probably, but I think you won't need to use PersistentHashMap just use whatever Map you use in Java and Clojure will work its magic when you call the function

👍 1
Dennis Tel 2020-12-16T10:45:48.092100Z

sounds good, thanks 🙂 !

2020-12-16T10:48:48.092300Z

there is another alternative that is working smoothly with clojure cli https://github.com/EwenG/badigeon

👍 1
2020-12-16T10:50:32.092600Z

(ns ops.java
  (:refer-clojure :exclude [compile])
  (:require [badigeon.javac :as javac]
            [badigeon.classpath :as classpath]))

(defn compile [{:keys [source-path output-path aliases]
                :or {aliases []}}]
  (javac/javac source-path
               {:compile-path output-path
                :javac-options ["-cp" (classpath/make-classpath {:aliases aliases})
                                "-target" "8" "-source" "8"
                                "-Xlint:-options"]}))
and then you can run it using -X option
:compile-java {:extra-deps {io.xapix/ops {:local/root "../ops/"}}
                          :exec-fn ops.java/compile
                          :exec-args {:source-path "src/java"
                                      :output-path "target/classes"}}
extra-deps here point to location of above namespace

Renato Alencar 2020-12-16T11:52:30.093300Z

Clojure implements Java native interfaces, such as Map, Iterable and Callable. So most of the time if you want use any interfaces of the java.lang package, Clojure implements it. Clojure runtime source code it's extremely simple to read, so you just directly read the implementation to understand how it works. https://github.com/clojure/clojure/tree/master/src/jvm/clojure/lang Also, Clojure has some shortcuts for the Java Reflection API, you could just call ancertors in a class in order to see its parent classes and implemented interfaces. For instance (ancestors (class {}))gives me this:

#{clojure.lang.IPersistentCollection clojure.lang.MapEquivalence clojure.lang.IMeta clojure.lang.ILookup clojure.lang.IEditableCollection java.util.Map clojure.lang.IMapIterable java.lang.Iterable java.util.concurrent.Callable java.io.Serializable clojure.lang.IFn java.lang.Object clojure.lang.IKVReduce clojure.lang.Seqable java.lang.Runnable clojure.lang.IHashEq clojure.lang.Associative clojure.lang.Counted clojure.lang.AFn clojure.lang.APersistentMap clojure.lang.IObj clojure.lang.IPersistentMap}

👍 2
Dennis Tel 2020-12-16T11:53:37.093500Z

ah thats a nice trick to know!

kwladyka 2020-12-16T12:52:49.098200Z

thank you, I will take a look on this

kwladyka 2020-12-16T12:53:19.098400Z

package api.logs;

import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.AbstractStringLayout;

import java.nio.charset.Charset;

@Plugin(name = "CustomLayout", category = "Core", elementType = "layout", printObject = true)
public class CustomLayout extends AbstractStringLayout {

    private static final String DEFAULT_EOL = "\r\n";

    protected CustomLayout(Charset charset) {
        super(charset);
    }

    @PluginFactory
    public static CustomLayout createLayout(@PluginAttribute(value = "charset", defaultString = "UTF-8") Charset charset) {
        return new CustomLayout(charset);
    }

    @Override
    public String toSerializable(LogEvent logEvent) {
        return logEvent.getMessage().getFormattedMessage() + DEFAULT_EOL;
    }
}
` Unless you will say code above is easy to modify into Clojure :)

2020-12-16T13:13:41.098700Z

imho, doesn’t worth a try) small helper like that is better to delegate to java

p-himik 2020-12-16T13:16:24.099200Z

If that was a simple class, I would've definitely used proxy. But since it uses Plugin, it can end up being arbitrarily complex. But I have no idea how Java annotations work under the hood - maybe it's something that you can do with proxy or something else as well.

2020-12-16T13:27:08.099400Z

require is known not to be thread safe. https://ask.clojure.org/index.php/9893/require-is-not-thread-safe

2020-12-16T13:27:43.099800Z

The discussion at that link contains some approaches to mitigate this property of require

kwladyka 2020-12-16T13:54:05.100Z

thank you for feedback, I was looking confirmation it is bad idea to do java->clojure for this 🙂

alexmiller 2020-12-16T14:04:50.102700Z

You can invoke the Clojure.read() function to read a literal map or Clojure.var() to look up Clojure vars and invoke them

kwladyka 2020-12-16T14:06:48.103Z

Do you know master of Java interops in Clojurians? 😉

msolli 2020-12-16T14:44:07.103200Z

Ah, this is exactly what I needed. Much obliged!

kwladyka 2020-12-16T14:56:46.103600Z

ech there are still issues around this, logging in structured JSON format in Clojure is a pain 🙂

alexmiller 2020-12-16T19:09:54.105500Z

has anyone tried Clojure 1.10.2-rc1? Any feedback good/bad?

lukasz 2020-12-16T19:34:41.106500Z

All tests pass in our biggest Clojure service, no issues whatsoever

lukasz 2020-12-16T19:34:54.106700Z

Will probably ship it to production next week

alexmiller 2020-12-16T19:35:21.106900Z

:thumbsup:

2020-12-16T19:50:47.107100Z

not to steal sean's thunder but we've got it in production on one service that got deployed yesterday, it should go out to the other services whenever they are next deployed

alexmiller 2020-12-16T19:55:06.107400Z

cool, thx

dharrigan 2020-12-16T19:56:27.107600Z

been using it in our test environment, nothing unusual to report.

alexmiller 2020-12-16T19:56:41.107800Z

anything usual?

dharrigan 2020-12-16T19:56:59.108Z

yes, it works 🙂

alexmiller 2020-12-16T19:57:06.108200Z

good :)

flowthing 2020-12-16T20:43:11.110400Z

Been using it in one of the apps I work on (not yet in prod, though) — everything’s been working as expected so far.

seancorfield 2020-12-16T20:43:20.110600Z

What hiredman said 🙂 We're on Alpha 4 for all our other production services so the differences are minimal for us.

seancorfield 2020-12-16T20:45:36.110800Z

(also as a comparative data point: we have 1.10.1.754 CLI on our production servers and 1.10.1.763 on dev and QA so that will also go to production with our next full deployment)