Inconsistent behaviour between lein run and REPL - clojure

I'm new to clojure and try to learn it by solving questions from https://www.4clojure.com.
The task is to implement the flatten function.
This is my implementation:
(ns clojure-noob.core
(:gen-class))
(defn -main
"I don't do a whole lot ... yet."
[]
(= ((fn [coll]
(let [flat (fn [coll]
(when-let [s (seq coll)]
(if (sequential? (first s))
(concat (flat (first s)) (flat (rest s)))
(cons (first s) (flat (rest s))))))]
(flat coll)))
'((1 2) 3 [4 [5 6]]))
'(1 2 3 4 5 6)))
When I run it in REPL like (-main) I get true.
When I run it as lein run I get this exception:
Exception in thread "main" java.lang.RuntimeException: Unable to resolve symbol: flat in this context, compiling:(clojure_noob/core.clj:11:42)
at clojure.lang.Compiler.analyze(Compiler.java:6688)
at clojure.lang.Compiler.analyze(Compiler.java:6625)
at clojure.lang.Compiler$InvokeExpr.parse(Compiler.java:3766)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6870)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyze(Compiler.java:6625)
at clojure.lang.Compiler$InvokeExpr.parse(Compiler.java:3834)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6870)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyze(Compiler.java:6625)
at clojure.lang.Compiler$IfExpr$Parser.parse(Compiler.java:2797)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6868)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyze(Compiler.java:6625)
at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:6001)
at clojure.lang.Compiler$LetExpr$Parser.parse(Compiler.java:6319)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6868)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6856)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyze(Compiler.java:6625)
at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:6001)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6868)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyze(Compiler.java:6625)
at clojure.lang.Compiler$IfExpr$Parser.parse(Compiler.java:2797)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6868)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6856)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyze(Compiler.java:6625)
at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:6001)
at clojure.lang.Compiler$LetExpr$Parser.parse(Compiler.java:6319)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6868)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6856)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6856)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyze(Compiler.java:6625)
at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:6001)
at clojure.lang.Compiler$FnMethod.parse(Compiler.java:5380)
at clojure.lang.Compiler$FnExpr.parse(Compiler.java:3972)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6866)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6856)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.access$300(Compiler.java:38)
at clojure.lang.Compiler$LetExpr$Parser.parse(Compiler.java:6269)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6868)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6856)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyze(Compiler.java:6625)
at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:6001)
at clojure.lang.Compiler$FnMethod.parse(Compiler.java:5380)
at clojure.lang.Compiler$FnExpr.parse(Compiler.java:3972)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6866)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6856)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyze(Compiler.java:6625)
at clojure.lang.Compiler$InvokeExpr.parse(Compiler.java:3766)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6870)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyze(Compiler.java:6625)
at clojure.lang.Compiler$HostExpr$Parser.parse(Compiler.java:1009)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6868)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyze(Compiler.java:6625)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6863)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyze(Compiler.java:6625)
at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:6001)
at clojure.lang.Compiler$FnMethod.parse(Compiler.java:5380)
at clojure.lang.Compiler$FnExpr.parse(Compiler.java:3972)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6866)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6856)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.access$300(Compiler.java:38)
at clojure.lang.Compiler$DefExpr$Parser.parse(Compiler.java:589)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6868)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyze(Compiler.java:6625)
at clojure.lang.Compiler.eval(Compiler.java:6931)
at clojure.lang.Compiler.load(Compiler.java:7379)
at clojure.lang.RT.loadResourceScript(RT.java:372)
at clojure.lang.RT.loadResourceScript(RT.java:363)
at clojure.lang.RT.load(RT.java:453)
at clojure.lang.RT.load(RT.java:419)
at clojure.core$load$fn__5677.invoke(core.clj:5893)
at clojure.core$load.invokeStatic(core.clj:5892)
at clojure.core$load.doInvoke(core.clj:5876)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5697)
at clojure.core$load_one.invoke(core.clj:5692)
at clojure.core$load_lib$fn__5626.invoke(core.clj:5737)
at clojure.core$load_lib.invokeStatic(core.clj:5736)
at clojure.core$load_lib.doInvoke(core.clj:5717)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:648)
at clojure.core$load_libs.invokeStatic(core.clj:5774)
at clojure.core$load_libs.doInvoke(core.clj:5758)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:648)
at clojure.core$require.invokeStatic(core.clj:5796)
at clojure.core$require.doInvoke(core.clj:5796)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at user$eval1036$fn__1038.invoke(form-init8418556840412273777.clj:1)
at user$eval1036.invokeStatic(form-init8418556840412273777.clj:1)
at user$eval1036.invoke(form-init8418556840412273777.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:6927)
at clojure.lang.Compiler.eval(Compiler.java:6917)
at clojure.lang.Compiler.load(Compiler.java:7379)
at clojure.lang.Compiler.loadFile(Compiler.java:7317)
at clojure.main$load_script.invokeStatic(main.clj:275)
at clojure.main$init_opt.invokeStatic(main.clj:277)
at clojure.main$init_opt.invoke(main.clj:277)
at clojure.main$initialize.invokeStatic(main.clj:308)
at clojure.main$null_opt.invokeStatic(main.clj:342)
at clojure.main$null_opt.invoke(main.clj:339)
at clojure.main$main.invokeStatic(main.clj:421)
at clojure.main$main.doInvoke(main.clj:384)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at clojure.lang.Var.invoke(Var.java:383)
at clojure.lang.AFn.applyToHelper(AFn.java:156)
at clojure.lang.Var.applyTo(Var.java:700)
at clojure.main.main(main.java:37)
Caused by: java.lang.RuntimeException: Unable to resolve symbol: flat in this context
at clojure.lang.Util.runtimeException(Util.java:221)
at clojure.lang.Compiler.resolveIn(Compiler.java:7164)
at clojure.lang.Compiler.resolve(Compiler.java:7108)
at clojure.lang.Compiler.analyzeSymbol(Compiler.java:7069)
at clojure.lang.Compiler.analyze(Compiler.java:6648)
... 128 more
I suspect it has something to do with namespaces, but I have no idea how to solve it.

Inside the let binding for flat, you refer to flat, which is not visible yet. There is an optional name argument to fn which you can use to resolve this issue.
(defn -main
"I don't do a whole lot ... yet."
[]
(= ((fn [coll]
(let [flat (fn flat [coll]
(when-let [s (seq coll)]
(if (sequential? (first s))
(concat (flat (first s)) (flat (rest s)))
(cons (first s) (flat (rest s))))))]
(flat coll)))
'((1 2) 3 [4 [5 6]]))
'(1 2 3 4 5 6)))
Also, the code can be simplified significantly without changing the meaning:
(defn -main
"I don't do a whole lot ... yet."
[]
(let [flat (fn flat [[el & els :as coll]]
(when coll
(if (sequential? el)
(concat (flat el) (flat els))
(cons el (flat els)))))]
(= (flat [[1 2] 3 [4 [5 6]]])
[1 2 3 4 5 6])))

Related

Why does adding metadata to a function definition work differently than other forms, in Clojure?

Why does this fail:
(eval (with-meta '(fn [] 0) {:stack (gensym "overflow")}))
; Syntax error compiling at (REPL:1:1).
; Unable to resolve symbol: overflow210 in this context
when none of the following fail?
(eval (with-meta '(do [] 0) {:stack (gensym "overflow")}))
; 0
(eval (with-meta '(let [] 0) {:stack (gensym "overflow")}))
; 0
(eval (with-meta '(if true 0 1) {:stack (gensym "overflow")}))
; 0
(eval (with-meta '(println "hello") {:stack (gensym "overflow")}))
; hello
; nil
The examples above are my attempt to find a minimal, reproducible example. I ran into this question when working on a macro, with a simplified example here:
(defmacro my-macro []
(with-meta '(fn [] 0) {:stack (gensym "overflow")}))
(my-macro)
; Syntax error compiling at (REPL:1:1).
; Unable to resolve symbol: overflow156 in this context
while attempting to follow the model explained in this post on testing Clojure macros.
Great question! This was fun to dig into.
It's helpful to first start by trying to set the metadata map to something that works, and retrieve it in each of your examples:
(meta (eval (with-meta '(fn [] 0) {:ten 10})))
;;=> {:ten 10}
(meta (eval (with-meta '(do [] 0) {:ten 10})))
;;=> nil
(meta (eval (with-meta '(let [] 0) {:ten 10})))
;;=> nil
(meta (eval (with-meta '(if true 0 1) {:ten 10})))
;;=> nil
(meta (eval (with-meta '(println "hello") {:ten 10})))
;; printed: hello
;;=> nil
Hopefully this gives you some idea of what's happening here: the metadata isn't returned as part of the value of the non-fn forms, so it's not evaluated. We can test this hypothesis with another value that uses its metadata, like a vector:
(meta (eval (with-meta '[1] {:ten 10})))
;;=> {:ten 10}
but with a gensym:
(eval (with-meta '[1] {:stack (gensym "overflow")}))
;;=> Syntax error compiling at (tmp:localhost:35479(clj)*:25:7).
;;=> Unable to resolve symbol: overflow6261 in this context
You can see where this is emitted in the Clojure compiler, and searching for new MetaExpr will show you the other places where metadata evaluation is emitted (I could see vectors, maps, sets, functions, and reify).
tl,dr: Clojure evaluates the metadata for function forms because it attaches the evaluated metadata to the resulting function. This is also true of the data literals supported in Clojure's syntax. Any other metadata on forms is stripped away by compilation, so it doesn't get evaluated and thus doesn't cause symbol resolution errors.
The problem is your usage of gensym, not the metadata. Observe:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(dotest
(binding [*print-meta* true]
(let [
fn-code-plain '(fn [] 42)
fn-code-meta (with-meta '(fn [] 42) {:alpha true})
fn-code-sym-meta (with-meta '(fn [] 42) {:alpha (gensym "dummy")})
]
(prn fn-code-plain)
(prn fn-code-meta)
\
(prn (eval fn-code-plain))
(prn (eval fn-code-meta))
(newline)
(prn fn-code-sym-meta)
(throws? (eval fn-code-sym-meta))
)
))
with result:
^{:line 7, :column 27} (fn [] 42)
^{:alpha true} (fn [] 42)
#object[user$eval19866$fn__19867 0xac891b5 "user$eval19866$fn__19867#ac891b5"]
^{:alpha true} #object[user$eval19870$fn__19871 0x2d1ab7e0 "user$eval19870$fn__19871#2d1ab7e0"]
^{:alpha dummy19865} (fn [] 42)
The problem is that eval sees the symbol dummy19865 and tries to resolve it. The fact that it was created by gensym is irrelevant. No problem with a keyword:
fn-code-kw-meta (with-meta '(fn [] 42) {:alpha :dummy-42})
<snip>
(prn fn-code-kw-meta)
(prn (eval fn-code-kw-meta))
producing:
^{:alpha :dummy-42} (fn [] 42)
^{:alpha :dummy-42} #object[user$eval19953$fn__19954 0x13253ac7 "user$eval19953$fn__19954#13253ac7"]
or a symbol that is defined:
(def mysym "Forty-Two!")
<snip>
fn-code-mysym-meta (with-meta '(fn [] 42) {:alpha mysym})
<snip>
(prn fn-code-mysym-meta)
(prn (eval fn-code-mysym-meta))
with result:
^{:alpha "Forty-Two!"} (fn [] 42)
^{:alpha "Forty-Two!"} #object[user$eval20082$fn__20083 0x24a63de5 "user$eval20082$fn__20083#24a63de5"]
Summary:
You have demonstrated that eval only attempts symbol resolution of metadata for the fn form, but not other special forms like do, let, if, or with pre-existing functions such as println. If you wish to explore further, you should probably inquire at the Clojure email list:
clojure#googlegroups.com
The above code is based on this template project.

Call to clojure.core/defn did not conform to spec

I have a small game that I'm writing in Clojure. I am very new to the language and one of the first errors I've stumbled upon is
Call to clojure.core/defn did not conform to spec.
I have no idea what causes the error, but an exception is thrown with this:
(ns survival-text.core
(:gen-class))
(def health 10)
(def hunger 10)
(def locations ["plains" "ocean" "mountains" "forest"])
(def currentLocation (atom 0))
(defn walk
(def random rand-int (count locations))
(reset! currentLocation (atom random)))
(defn choices
[input]
(case (input)
"walk" (walk)
"exit" (System/exit 0)
""))
(defn -main
[& args]
(while true
(def input read-line)
(choices input)))
But not this:
(ns survival-text.core
(:gen-class))
(def health 10)
(def hunger 10)
(def locations ["plains" "ocean" "mountains" "forest"])
(def currentLocation (atom 0))
(defn choices
[input]
(case (input)
"walk" (reset! currentLocation (atom (rand-nth locations)))
"exit" (System/exit 0)
""))
(defn -main
[& args]
(while true
(def input read-line)
(choices input)))
This is what is being thrown at me
Exception in thread "main" Syntax error macroexpanding clojure.core/defn at (survival_text/core.clj:9:1).
Call to clojure.core/defn did not conform to spec.
at clojure.lang.Compiler.checkSpecs(Compiler.java:6971)
at clojure.lang.Compiler.macroexpand1(Compiler.java:6987)
at clojure.lang.Compiler.macroexpand(Compiler.java:7074)
at clojure.lang.Compiler.eval(Compiler.java:7160)
at clojure.lang.Compiler.load(Compiler.java:7635)
at clojure.lang.RT.loadResourceScript(RT.java:381)
at clojure.lang.RT.loadResourceScript(RT.java:372)
at clojure.lang.RT.load(RT.java:463)
at clojure.lang.RT.load(RT.java:428)
at clojure.core$load$fn__6824.invoke(core.clj:6126)
at clojure.core$load.invokeStatic(core.clj:6125)
at clojure.core$load.doInvoke(core.clj:6109)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5908)
at clojure.core$load_one.invoke(core.clj:5903)
at clojure.core$load_lib$fn__6765.invoke(core.clj:5948)
at clojure.core$load_lib.invokeStatic(core.clj:5947)
at clojure.core$load_lib.doInvoke(core.clj:5928)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:667)
at clojure.core$load_libs.invokeStatic(core.clj:5985)
at clojure.core$load_libs.doInvoke(core.clj:5969)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:667)
at clojure.core$require.invokeStatic(core.clj:6007)
at clojure.core$require.doInvoke(core.clj:6007)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at user$eval140$fn__144.invoke(form-init1707647033589713549.clj:1)
at user$eval140.invokeStatic(form-init1707647033589713549.clj:1)
at user$eval140.invoke(form-init1707647033589713549.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:7176)
at clojure.lang.Compiler.eval(Compiler.java:7166)
at clojure.lang.Compiler.load(Compiler.java:7635)
at clojure.lang.Compiler.loadFile(Compiler.java:7573)
at clojure.main$load_script.invokeStatic(main.clj:452)
at clojure.main$init_opt.invokeStatic(main.clj:454)
at clojure.main$init_opt.invoke(main.clj:454)
at clojure.main$initialize.invokeStatic(main.clj:485)
at clojure.main$null_opt.invokeStatic(main.clj:519)
at clojure.main$null_opt.invoke(main.clj:516)
at clojure.main$main.invokeStatic(main.clj:598)
at clojure.main$main.doInvoke(main.clj:561)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.lang.Var.applyTo(Var.java:705)
at clojure.main.main(main.java:37)
Caused by: clojure.lang.ExceptionInfo: Call to clojure.core/defn did not conform to spec. {:clojure.spec.alpha/problems ({:path [:fn-tail :arity-1 :params], :pred clojure.core/vector?, :val (def random rand-int (count locations)), :via [:clojure.core.specs.alpha/defn-args :clojure.core.specs.alpha/params+body :clojure.core.specs.alpha/param-list :clojure.core.specs.alpha/param-list], :in [1]} {:path [:fn-tail :arity-n :bodies :params], :pred clojure.core/vector?, :val def, :via [:clojure.core.specs.alpha/defn-args :clojure.core.specs.alpha/params+body :clojure.core.specs.alpha/params+body :clojure.core.specs.alpha/params+body :clojure.core.specs.alpha/param-list :clojure.core.specs.alpha/param-list], :in [1 0]}), :clojure.spec.alpha/spec #object[clojure.spec.alpha$regex_spec_impl$reify__2509 0x41e68d87 "clojure.spec.alpha$regex_spec_impl$reify__2509#41e68d87"], :clojure.spec.alpha/value (walk (def random rand-int (count locations)) (reset! currentLocation (atom random))), :clojure.spec.alpha/args (walk (def random rand-int (count locations)) (reset! currentLocation (atom random)))}
at clojure.spec.alpha$macroexpand_check.invokeStatic(alpha.clj:705)
at clojure.spec.alpha$macroexpand_check.invoke(alpha.clj:697)
at clojure.lang.AFn.applyToHelper(AFn.java:156)
at clojure.lang.AFn.applyTo(AFn.java:144)
at clojure.lang.Var.applyTo(Var.java:705)
at clojure.lang.Compiler.checkSpecs(Compiler.java:6969)
... 44 more
I am using leiningen and I haven't changed anything an all apart from the src/survival-text/core.clj
What causes the error and how do I fix it?
You forgot the args list. It should look like:
(defn walk
[] ; no args to this function
(let [random (rand-int (count locations))]
(reset! currentLocation random)))
You may also want to review Clojure intro material like Brave Clojure or Getting Clojure for an introduction. In particular, it is generally not a good idea to use (def ...) inside of a function.

Issue when compiling

I keep getting the following error, its most likely something obvious but i'm rather new to this. Any help would be appreciated. I'm quite sure there error is in this section of the code.
(b/defcomponent client {:bounce/deps #{config/value}}
(log/info "Connecting to Discord")
(let [token (get-in config/value [:discord :token])
client (if token
(.. (ClientBuilder.)
(withToken token)
login)
(throw (Error. "Discord token not found, please set {:discord {:token \"...\"}} in `config/config.edn`.")))]
Had a look through where i believe the error is, cant seem to figure it out though.
(ns snowball.discord
(:require [clojure.string :as str]
[clojure.core.async :as a]
[bounce.system :as b]
[taoensso.timbre :as log]
[camel-snake-kebab.core :as csk]
[snowball.config :as config]
[snowball.util :as util])
(:import [sx.blah.discord.api ClientBuilder]
[sx.blah.discord.util.audio AudioPlayer]
[sx.blah.discord.handle.audio IAudioReceiver]
[sx.blah.discord.api.events IListener]))
(defn event->keyword [c]
(-> (str c)
(str/split #"\.")
(last)
(str/replace #"Event.*$" "")
(csk/->kebab-case-keyword)))
(defmulti handle-event! (fn [c] (event->keyword c)))
(defmethod handle-event! :default [_] nil)
(declare client)
(defn ready? []
(some-> client .isReady))
(defn poll-until-ready []
(let [poll-ms (get-in config/value [:discord :poll-ms])]
(log/info "Connected, waiting until ready")
(util/poll-while poll-ms (complement ready?) #(log/info "Not ready, sleeping for" (str poll-ms "ms")))
(log/info "Ready")))
(defn channels []
(some-> client .getVoiceChannels seq))
(defn channel-users [channel]
(some-> channel .getConnectedUsers seq))
(defn current-channel []
(some-> client .getConnectedVoiceChannels seq first))
(defn ->name [entity]
(some-> entity .getName))
(defn ->id [entity]
(some-> entity .getLongID))
(defn leave! [channel]
(when channel
(log/info "Leaving" (->name channel))
(.leave channel)))
(defn join! [channel]
(when channel
(log/info "Joining" (->name channel))
(.join channel)))
(defn bot? [user]
(some-> user .isBot))
(defn muted? [user]
(when user
(let [voice-state (first (.. user getVoiceStates values))]
(or (.isMuted voice-state)
(.isSelfMuted voice-state)
(.isSuppressed voice-state)))))
(defn can-speak? [user]
(not (or (bot? user) (muted? user))))
(defn has-speaking-users? [channel]
(->> (channel-users channel)
(filter can-speak?)
(seq)
(boolean)))
(defn default-guild []
(some-> client .getGuilds seq first))
(defn guild-users []
(some-> (default-guild) .getUsers))
(defn guild-text-channels []
(some-> (default-guild) .getChannels))
(defn guild-voice-channels []
(some-> (default-guild) .getVoiceChannels))
(defn move-user-to-voice-channel [user channel]
(when (and user channel)
(try
(.moveToVoiceChannel user channel)
(catch Exception e
(log/warn "Tried to move a user to a voice channel that isn't connected to voice already")))))
(defn play! [audio]
(when audio
(when-let [guild (default-guild)]
(doto (AudioPlayer/getAudioPlayerForGuild guild)
(.clear)
(.queue audio)))))
(defn send! [channel-id message]
(when-let [channel (.getChannelByID (default-guild) channel-id)]
(log/info "Sending message to" channel-id "-" message)
(.sendMessage channel message)))
(defmethod handle-event! :reconnect-success [_]
(log/info "Reconnection detected, leaving any existing voice channels to avoid weird state")
(poll-until-ready)
(when-let [channel (current-channel)]
(leave! channel)))
(defn audio-manager []
(some-> (default-guild) .getAudioManager))
(defrecord AudioEvent [audio user])
(defn subscribe-audio! [f]
(let [sub! (atom nil)
closed?! (atom false)
sub-chan (a/go-loop []
(when-not #closed?!
(a/<! (a/timeout (get-in config/value [:discord :poll-ms])))
(if-let [am (audio-manager)]
(try
(let [sub (reify IAudioReceiver
(receive [_ audio user _ _]
(try
(when-not (bot? user)
(f (AudioEvent. audio user)))
(catch Exception e
(log/error "Caught error while passing audio event to subscription handler" e)))))]
(reset! sub! sub)
(log/info "Audio manager exists, subscribing to audio")
(.subscribeReceiver am sub))
(catch Exception e
(log/error "Caught error while setting up audio subscription" e)))
(recur))))]
(fn []
(when-let [sub #sub!]
(reset! closed?! true)
(a/close! sub-chan)
(.unsubscribeReceiver (audio-manager) sub)))))
(b/defcomponent client {:bounce/deps #{config/value}}
(log/info "Connecting to Discord")
(let [token (get-in config/value [:discord :token])
client (if token
(.. (ClientBuilder.)
(withToken token)
login)
(throw (Error. "Discord token not found, please set {:discord {:token \"...\"}} in `config/config.edn`.")))]
(.registerListener
(.getDispatcher client)
(reify IListener
(handle [_ event]
(handle-event! event))))
(with-redefs [client client]
(poll-until-ready))
(b/with-stop client
(log/info "Shutting down Discord connection")
(.logout client))))
(b/defcomponent audio-chan {:bounce/deps #{client}}
(log/info "Starting audio channel from subscription")
(let [audio-chan (a/chan (a/sliding-buffer 100))
sub (subscribe-audio!
(fn [event]
(a/go
(a/put! audio-chan event))))]
(b/with-stop audio-chan
(log/info "Closing audio channel and unsubscribing")
(sub)
(a/close! audio-chan))))
Below is the error i am getting
> make
> ./run.sh
> 19-05-25 19:05:45 localhost INFO [snowball.main:6] - Starting components...
> 19-05-25 19:05:47 localhost INFO [snowball.config:16] - Loading base config from config.base.edn and user config from
> config/config.edn
> 19-05-25 19:05:47 localhost INFO [snowball.discord:150] - Connecting to Discord
> Exception in thread "main" java.lang.ClassCastException: class clojure.lang.Symbol cannot be cast to class java.lang.String
> (clojure.lang.Symbol is in unnamed module of loader 'app';
> java.lang.String is in module java.base of loader 'bootstrap')
> at snowball.discord$eval12963$start_client__12964.invoke(discord.clj:152)
> at clojure.lang.AFn.applyToHelper(AFn.java:152)
> at clojure.lang.AFn.applyTo(AFn.java:144)
> at clojure.core$apply.invokeStatic(core.clj:665)
> at clojure.core$apply.invoke(core.clj:660)
> at bounce.system$start_system$fn__479$fn__480.invoke(system.clj:68)
> at bounce.system$start_system$fn__479$fn__480$fn__481.invoke(system.clj:71)
> at clojure.lang.AFn.applyToHelper(AFn.java:152)
> at clojure.lang.AFn.applyTo(AFn.java:144)
> at clojure.core$apply.invokeStatic(core.clj:665)
> at clojure.core$with_bindings_STAR_.invokeStatic(core.clj:1973)
> at clojure.core$with_bindings_STAR_.doInvoke(core.clj:1973)
> at clojure.lang.RestFn.invoke(RestFn.java:425)
> at bounce.system$start_system$fn__479$fn__480.invoke(system.clj:69)
> at bounce.system$start_system.invokeStatic(system.clj:80)
> at bounce.system$start_system.invoke(system.clj:59)
> at bounce.system$start_BANG_.invokeStatic(system.clj:122)
> at bounce.system$start_BANG_.invoke(system.clj:112)
> at snowball.main$_main.invokeStatic(main.clj:13)
> at snowball.main$_main.invoke(main.clj:5)
> at clojure.lang.AFn.applyToHelper(AFn.java:152)
> at clojure.lang.AFn.applyTo(AFn.java:144)
> at clojure.lang.Var.applyTo(Var.java:705)
> at clojure.core$apply.invokeStatic(core.clj:665)
> at clojure.main$main_opt.invokeStatic(main.clj:491)
> at clojure.main$main_opt.invoke(main.clj:487)
> at clojure.main$main.invokeStatic(main.clj:598)
> at clojure.main$main.doInvoke(main.clj:561)
> at clojure.lang.RestFn.applyTo(RestFn.java:137)
> at clojure.lang.Var.applyTo(Var.java:705)
> at clojure.main.main(main.java:37)
> Makefile:12: recipe for target 'run' failed
> make: *** [run] Error 1
From this bit:
> 19-05-25 19:05:47 localhost INFO [snowball.discord:150] - Connecting to Discord
> Exception in thread "main" java.lang.ClassCastException: class clojure.lang.Symbol cannot be cast to class java.lang.String
It looks like the expression (get-in config/value [:discord :token]) in line 152 is returning a symbol (a clojure.lang.Symbol) instead of a String, and that is causing a ClassCastException to be thrown when building the Client class.
I guess what's going on is that you are reading the configuration from an external file or some other source and your token is not properly quoted, so it's being resolved as a Symbol ("a variable name") instead of a String.

issue in clojure macro creating symbols for each element in map array

Basically I'm new to macro and trying in macro to create symbols from array of maps and getting error: CompilerException java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol
But am able to make it work for map instead of array of maps and calling (gensym-of-map ~args ~ctx)
Please help how to solve gensym-of-amap macro
(defmacro gensym-of-amap[args ctx]
`(let [~#(mapcat (fn [[k v]] [k `~v]) ctx)]
(prn "access ~args via ~'aa ~'bb and ctx syms ~'a"))
)
(defmacro gensyms[ctx args]
`(let [] (fn ~args
(for [~'c '~ctx]
(gensym-of-amap ~args ~'c) ;run foreach ctx
))))
(def test-macro (gensyms [{a 1} {a 2}] [aa bb]))
(apply test-macro [1 2])

Where is my Null Pointer Exception - Clojure edition

I am getting the following stacktrace when running the command: lein run "this is the other different thing" "this,different,other"
Stacktrace
Exception in thread "main" java.lang.NullPointerException, compiling:(/private/var/folders/y8/6lt_81xn47d4n2k641z52rg00000gn/T/form-init8328218573408236617.clj:1:125)
at clojure.lang.Compiler.load(Compiler.java:7391)
at clojure.lang.Compiler.loadFile(Compiler.java:7317)
at clojure.main$load_script.invokeStatic(main.clj:275)
at clojure.main$init_opt.invokeStatic(main.clj:277)
at clojure.main$init_opt.invoke(main.clj:277)
at clojure.main$initialize.invokeStatic(main.clj:308)
at clojure.main$null_opt.invokeStatic(main.clj:342)
at clojure.main$null_opt.invoke(main.clj:339)
at clojure.main$main.invokeStatic(main.clj:421)
at clojure.main$main.doInvoke(main.clj:384)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at clojure.lang.Var.invoke(Var.java:383)
at clojure.lang.AFn.applyToHelper(AFn.java:156)
at clojure.lang.Var.applyTo(Var.java:700)
at clojure.main.main(main.java:37)
Caused by: java.lang.NullPointerException
at clojure.string$replace.invokeStatic(string.clj:101)
at clojure.string$replace.invoke(string.clj:75)
at redact.core$redact_doc.invokeStatic(core.clj:12)
at redact.core$redact_doc.invoke(core.clj:7)
at redact.core$_main.invokeStatic(core.clj:54)
at redact.core$_main.doInvoke(core.clj:50)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at clojure.lang.Var.invoke(Var.java:383)
at user$eval5.invokeStatic(form-init8328218573408236617.clj:1)
at user$eval5.invoke(form-init8328218573408236617.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:6927)
at clojure.lang.Compiler.eval(Compiler.java:6917)
at clojure.lang.Compiler.load(Compiler.java:7379)
... 14 more
And here is my code:
(ns redact.core
(:gen-class)
(:require [clojure.java.io :as io]
[clojure.string :as str]
))
(defn redact-doc
;; Reads the file line by line and redacts all the matched words
([target stoplist]
(if (empty? stoplist)
(str/trim target)
(redact-doc (str/replace target (re-pattern (str "\\s(" (first stoplist) ")(\\s|$)")) " REDACTED ") (rest stoplist))))
)
(defn get-target-text
;; Takes a vector of args and returns a String of a text file or and sentances
([args] (get-target-text args ""))
([args result]
(if (empty? args)
result
(get-target-text (rest args) (if (boolean (re-find #"(.+\.[^csv\s])" (first args)))
(str result (slurp (first args)))
(if (not (boolean (re-find #"(.+\.csv|.+,.+)" (first args))))
(if (boolean (re-find #"\s" (str/trim (first args))))
(str result (first args) " ")))))))
)
(defn read-csv
;; Takes in a filename and returns a vector of the csv values
[file-name]
(str/split (with-open [rdr (io/reader file-name)]
(doall (reduce str (line-seq rdr)))) #","))
(defn gen-stoplist
;; Generates the stoplist for words to be redacted
([args] (gen-stoplist args []))
([args stoplist]
(if (empty? args)
stoplist
(gen-stoplist (rest args) (if (boolean (re-find #"(.+\.csv)" (first args)))
(into [] (concat stoplist (read-csv (first args))))
(if (boolean (re-find #"(.+\..[^csv\s])" (first args)))
stoplist
(if (boolean (re-find #"(.*,.*)" (first args)))
(into [] (concat stoplist (str/split (first args) #",")))
(if (boolean (re-find #"(\s)" (str/trim (first args))))
stoplist
(into [] (concat stoplist [(first args)] ))))))))))
(defn -main
([& args]
(def stoplist (gen-stoplist args))
(def target-text (get-target-text args))
(println (redact-doc target-text stoplist)))
)
I have been staring at this trying to figure out what is causing the issue. I have tested all of the methods independently on the REPL and they all seem to work but the (-main) method is throwing a null pointer exception on the str/replace call....just not sure why. Any help you can give is much appreciated. Thanks!
There is a fair bit about your code which is not really correct. My guess is
that our getting that call because your calling a function which is expecting a
value and is getting a nil passed in - my guess would be one of the string
functions.
your function definitions are not quite right. If your function only has a
single 'signature' then you don't need the additional brackets. You should also
use let bindings inside rather than def. e.g.
(defn -main
[& args]
(let [stoplist (gen-stoplist args)
target-text (get-target-text args))]
(println (redact-doc target-text stoplist)))
Your code is not passing what you think to gen-stoplist or get-target-text. I
suspect the null pointer is because of the call to str/trim being passed a nil
rather than a string.
My suggestion would be to open a repl and interact with it using some println in
your functions to look at what is getting parsed in.