Fork me on GitHub
#beginners
<
2021-02-04
>
West04:02:00

I’m trying to create a hyphenated namespace. I’m sure others have done it, why does it give an error that my namespace is invalid?

Alex Miller (Clojure team)04:02:45

if the namespace is hyphenated, the file name should use _

Alex Miller (Clojure team)04:02:15

might want to post the error if that doesn't sound like the problem

solf04:02:18

It's the garden/garden that's wrong, no namespaced symbols

solf04:02:30

I think you want garden.core :as garden

3
seancorfield04:02:07

If you scroll that long error message, it should tell you more of what it didn't like and what part of the code it didn't like.

West04:02:00

Ah man, that was so stupid.

West04:02:24

I didn’t want to scroll though all the clojure spec errors.

seancorfield04:02:17

The editor put a warning wiggly line under garden/garden

seancorfield04:02:04

I tried that ns form in a plain REPL and it's much more obvious:

seanc@DESKTOP-30ICA76:~/clojure$ clj
Clojure 1.10.2
user=> (ns wildwestrom.tailwind-garden
         (:gen-class)
         (:require [clj-css.core :as parser]
                   [garden/garden :as garden]))
Syntax error macroexpanding clojure.core/ns at (REPL:1:1).
garden/garden - failed: simple-symbol? at: [:ns-clauses :require :body :libspec :lib+opts :lib] spec: :clojure.core.specs.alpha/libspec
[garden/garden :as garden] - failed: simple-symbol? at: [:ns-clauses :require :body :libspec :lib] spec: :clojure.core.specs.alpha/libspec
garden/garden - failed: simple-symbol? at: [:ns-clauses :require :body :prefix-list :prefix] spec: :clojure.core.specs.alpha/prefix-list
[garden/garden :as garden] - failed: #{:verbose :reload :reload-all} at: [:ns-clauses :require :body :flag] spec: :clojure.core.specs.alpha/ns-require
:require - failed: #{:refer-clojure} at: [:ns-clauses :refer-clojure :clause] spec: :clojure.core.specs.alpha/ns-refer-clojure
:require - failed: #{:import} at: [:ns-clauses :import :clause] spec: :clojure.core.specs.alpha/ns-import
:require - failed: #{:use} at: [:ns-clauses :use :clause] spec: :clojure.core.specs.alpha/ns-use
:require - failed: #{:refer} at: [:ns-clauses :refer :clause] spec: :clojure.core.specs.alpha/ns-refer
:require - failed: #{:load} at: [:ns-clauses :load :clause] spec: :clojure.core.specs.alpha/ns-load
:require - failed: #{:gen-class} at: [:ns-clauses :gen-class :clause] spec: :clojure.core.specs.alpha/ns-gen-class
user=>

seancorfield04:02:44

And you can see that garden/garden failed simple-symbol?

Alex Miller (Clojure team)04:02:27

_wonders why cider doesn't use the <https://clojure.org/reference/repl_and_main#_at_repl|available tools> in clojure.main to print the triaged error message rather than the explain data_

seancorfield04:02:01

Maybe it just hasn't caught up with that yet? Or maybe it tries to do something that is portable across older versions of Clojure?

Alex Miller (Clojure team)04:02:03

I talked to the cider folks about this 2 years ago

Alex Miller (Clojure team)04:02:35

(I'm salty about complaints about error messages when tools don't display the results of work 2 years old)

Alex Miller (Clojure team)04:02:04

the whole point of that work was to avoid showing everything that cider is showing

caumond05:02:03

Hi @U064X3EF3, could you be more specific ? Is it possible to enjoy those improvements ?

solf05:02:14

If you use the default clojure repl, you'll get the new error messages, if you use a specific tool/ide, you'll have to refer to them. About CIDER specifically, I think @U064X3EF3 is referring to this (unresolved) issue: https://github.com/clojure-emacs/cider/issues/2443

seancorfield06:02:16

@U018QDQGZ9Q The plain Clojure REPL has all these improvements. Any tooling that has decided to take advantage of the additional "triage" functionality and the extra data that has been attached to all the exceptions will have all these improvements. CIDER has not incorporated that yet.

West04:02:15

For some reason it says my namespace is written wrong. What the hell? I have everything correct.

popeye12:02:48

I have an atom object as below

#object[clojure.lang.Atom 0x432fca40 {:key :true, 
:val {a "a", 
:b "b"}}]

popeye12:02:09

How can I remove :b from atom?

Lennart Buit12:02:21

(swap! my-atom dissoc :b)

borkdude12:02:34

@popeyepwr (swap! the-atom dissoc :b)

popeye12:02:15

this has :val key inside we have 2 more keys :a and :b @borkdude @lennart.buit

borkdude12:02:22

oh, (swap! the-atom update :val dissoc :b)

popeye12:02:27

its is not removing

borkdude12:02:46

Sure enough:

(swap! (atom {:val {:a 1 :b 2}}) update :val dissoc :b)
{:val {:a 1}}
I notice your a key is not a keyword but a symbol though

popeye12:02:22

how to remove if it is a symbol?

popeye12:02:35

in my code I have symbol

borkdude12:02:00

replace :b with 'b

Marcus14:02:49

which library would you use to access apache kafka?

R.A. Porter14:02:08

You might want to look at Ketu (https://github.com/AppsFlyer/ketu). I haven’t used it yet, but it’s intriguing. My current project is using Jackdaw (https://github.com/FundingCircle/jackdaw) which works just fine.

Marcus14:02:20

Nice! Thank you very much 🙂 I will look into both.

Marcus14:02:22

Why would you consider ketu instead of jackdaw?

R.A. Porter14:02:51

From the video posted recently, it’s a pure Clojure solution. https://www.youtube.com/watch?v=zAlvM33UpGA

R.A. Porter14:02:35

But again, I have no experience with it so can’t attest to its readiness.

Marcus14:02:14

ok. thanks again!

Marcus14:02:27

I'll test ketu and see how it goes 🙂

V14:02:06

Any tips on how to avoid huge let bindings? Also nested lets

caumond14:02:26

Smaller functions ? Are you sure you cant breakdown the function ?

caumond14:02:28

In other words for me it is a smell for a too big functions

lepistane14:02:22

and once u break it in functions u can use threading macro -> or ->>

👍 3
blak3mill3r15:02:10

smaller functions and fewer named things; huge let blocks are a symptom of thinking procedurally

blak3mill3r15:02:40

I think it takes some getting used to, relaxing the requirement to give each intermediate step a name; you might get something out of this presentation https://www.infoq.com/presentations/Macros-Monads/

Sam Bauch15:02:35

hi folks! 👋 brand new to clojure and attempting to put together a demo app for consuming an OAuth server that I maintain. I feel like I'm rather close, but am not quite understanding how to close the loop. I'm using https://github.com/weavejester/ring-oauth2 and don't quite understand the part about accessing the access_token in order to request a user's profile. There's also an error in my logs that im not quite sure what to make of, but i think it might be related to favicon request? here's what I've got so far - https://github.com/enterprise-oss/osso-clojure-example/blob/main/src/clojure_osso_demo/web.clj just hoping one of you kind folks might point me in the right direction to get this over the finish line

javahippie15:02:40

Can you post the error from your logs? Which Oauth Provider are you connection against, what implementation is there? Aah I see, Osso

Sam Bauch15:02:04

here's the logs:

2021-02-04 10:24:33.108:WARN:oejs.HttpChannel:qtp871565652-27: /favicon.ico
java.lang.IllegalArgumentException: contains? not supported on type: compojure.core$routes$fn__4160
        at clojure.lang.RT.contains(RT.java:849)
        at clojure.core$contains_QMARK_.invokeStatic(core.clj:1492)
        at clojure.core$contains_QMARK_.invoke(core.clj:1484)
        at ring.middleware.flash$flash_response.invokeStatic(flash.clj:21)
        at ring.middleware.flash$flash_response.invoke(flash.clj:14)
        at ring.middleware.flash$wrap_flash$fn__5653.invoke(flash.clj:39)
        at ring.middleware.session$wrap_session$fn__5630.invoke(session.clj:108)
        at ring.middleware.keyword_params$wrap_keyword_params$fn__4383.invoke(keyword_params.clj:36)
        at ring.middleware.nested_params$wrap_nested_params$fn__4441.invoke(nested_params.clj:89)
        at ring.middleware.multipart_params$wrap_multipart_params$fn__4523.invoke(multipart_params.clj:173)
        at ring.middleware.params$wrap_params$fn__4343.invoke(params.clj:67)
        at ring.middleware.cookies$wrap_cookies$fn__5501.invoke(cookies.clj:175)
        at ring.middleware.absolute_redirects$wrap_absolute_redirects$fn__6245.invoke(absolute_redirects.clj:47)
        at ring.middleware.resource$wrap_resource$fn__6125.invoke(resource.clj:37)
        at ring.middleware.content_type$wrap_content_type$fn__6193.invoke(content_type.clj:34)
        at ring.middleware.default_charset$wrap_default_charset$fn__6217.invoke(default_charset.clj:31)
        at ring.middleware.not_modified$wrap_not_modified$fn__6174.invoke(not_modified.clj:53)
        at ring.middleware.x_headers$wrap_x_header$fn__5927.invoke(x_headers.clj:22)
        at ring.middleware.x_headers$wrap_x_header$fn__5927.invoke(x_headers.clj:22)
        at ring.middleware.x_headers$wrap_x_header$fn__5927.invoke(x_headers.clj:22)
        at ring.adapter.jetty$proxy_handler$fn__5858.invoke(jetty.clj:27)
        at ring.adapter.jetty.proxy$org.eclipse.jetty.server.handler.AbstractHandler$ff19274a.handle(Unknown Source)
        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
        at org.eclipse.jetty.server.Server.handle(Server.java:501)
        at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:383)
        at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:556)
        at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:375)
        at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:273)
        at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311)
        at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105)
        at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:336)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:313)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:171)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:129)
        at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:375)
        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:806)
        at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:938)
        at java.base/java.lang.Thread.run(Thread.java:832)

Sam Bauch15:02:45

and the Oauth service is my OSS app Osso, we have a demo instance at https://demo.ossoapp.com that im using here (the Demo Production OAuth client)

Sam Bauch15:02:12

im able to get sent to my OAuth server and am sent back to my application, and I see a request to the token endpoint on my oauth server

javahippie15:02:47

I’m not sure if the error is related to OAuth, as I don’t see the ring-oauth middleware in the stacktrace

Sam Bauch15:02:02

ok, that sounds good!

Sam Bauch15:02:54

so that lib allows me to redirect to a url, where I suppose I would then write something to use the access token to make a request to my oauth server's /oauth/me endpoint

Sam Bauch15:02:33

they say > the way you'd access the token would be as follows: > (-> request :oauth2/access-tokens :osso) and im not really sure what to make of that?

javahippie15:02:32

I used this in an app for Google Login (I can’t share the code), let me try and remember. The auth process in your app should be started, as soon as the user accesses /auth/osso, right? The user then gets redirected to the Oauth Provider, to log in

javahippie15:02:00

After login is done, they are redirected to /auth/osso/callback, a URL you need to provide in your application

javahippie15:02:40

You will then get a request parameter from that call, that contains a session with the token information that is sent too you by your OAuth Provider

javahippie15:02:57

The key should be something like :oauth2/access-tokens. This will get you a map with the key :osso, and the tokens provided

javahippie15:02:08

I think the error lies in your Compojure and middleware setup, that’s not correct

javahippie15:02:15

You need to set up the routes, and wrap them with your middleware, in your code you have added wrap-oauth2 to the routes call of Compojure.

javahippie16:02:12

I cannot run that code right now (at the wrong machine), but copy/pasted from your GitHub example, this is the direction you might want to take

Sam Bauch16:02:59

ok yup that helped get me pointed in the right direction for addressing that error, it gone! thanks so much!

Sam Bauch16:02:26

now I need to figure out how to read a request parameter, and then use that token to make a request, which seems figure-out-able

👍 3
Sam Bauch17:02:04

ok cool! got it working, printing out the json to the client! naturally I'm sure a lot of this can get cleaned up, and i still have some nice-to-haves i'll try and figure out at some point, but cool! im a clojure dev now 😎

Sam Bauch17:02:54

thanks much for your help Tim!!

javahippie18:02:37

Happy to hear that! 🙂

lepistane15:02:48

i am using web sockets to receive events from an api and write parsed ones to s3 but i want to decouple receiving and writing to s3 i am doing (async/put!... in on-receive handler and i created this fn to do the s3 bit

(defn loader [in]
  (async/go
    (while true
      (let [msg (async/<! in)]
        (store-in-s3 msg)))))
If i understood correctly the loader will take messages as they come from the channel and do the 'writing' and on-receive can put to the channel as fast as possible. I am just looking for confirmation is this correct or i misunderstood how async/go operates?

hiredman17:02:20

In general my advice is to not use put!

hiredman17:02:28

When beginners use put! they pretty much always leave out the callback argument, which is very bad

hiredman17:02:53

And many apis don't give you anything sensible to do using the callback

hiredman17:02:30

Using !!> will block the current thread to communicate back pressure without a callback

hiredman17:02:39

unless you are using cljs, in which case you are screwed, because despite the necessity of callback based apis there, people keep writing them in a such a way as to not let you properly handle backpressure

blak3mill3r16:02:20

that looks like it will basically work; however one thing to beware of is that store-in-s3 is likely a function which blocks the thread

blak3mill3r16:02:53

core.async uses a thread pool under the hood (a fixed size pool onto which ready go-blocks are scheduled)

blak3mill3r16:02:36

if you block all of those threads calling store-in-s3 then no other go-block gets to run; this is not necessarily a show-stopper problem for what you're doing

blak3mill3r16:02:02

but it is important to understand that you could theoretically get into a kind of deadlock situation doing things like this

blak3mill3r16:02:00

if all you want to do is store-in-s3 for each message on a channel, AFAP, this ^ is a good way to do that

👍 3
chrisblom16:02:55

An alternative to using pipeline-blocking is to wrap store-in-s3 with async/thread so that it is executed in another thread pool separate from the one core.async uses to schedule it's go-blocks

(defn loader [in]
  (async/go
    (while true
      (let [msg (async/<! in)]
        (async/<! (async/thread (store-in-s3 msg)))))))

hiredman16:02:50

Pipeline and pipeline blocking both use thread internally as well

hiredman17:02:08

The diff between in pipeline and this code is not the use of thread, but this code ignores the return value from uploading to s3, while pipeline expects it to be a useful value to passed down to some consumer

blak3mill3r17:02:49

pipeline-blocking lets you control the concurrency explicitly as well

blak3mill3r17:02:11

s/lets you/forces you to/

lepistane08:02:08

thank you for the answers but i am not sure how to use the pipeline or pipeline blocking in my case could u walk me through the process how to use this and think about this? I found https://gist.github.com/raspasov/7c9d8f2872d6065b2145 but it's not obvious what i should do

lepistane21:02:09

@UC681SR17 @U0NCTKEV8 u must've not seen question above that's why i am tagging. Could u tell me how to use pipline as it's not obvious to me or where could i learn about it and how it's used in detail and practical examples?

blak3mill3r01:02:20

@U45SLGVHV this code calls long-blocking-op in parallel with 18 threads, passing each of values to it (and thus takes just barely more than 100ms)

(def c-in (a/chan 10))
(def c-out (a/chan 10))
(defn long-blocking-op [v] (Thread/sleep 100) [:result v])
(a/pipeline-blocking 18 c-out (map long-blocking-op) c-in)
(time
 (let [values ["bang" "whiz" "pop" "Pow" "zow" "biff" "zing"]]
   (a/onto-chan c-in values)
   ;; read the same number of outputs blocking this thread on each one
   (doseq [v values] (println (a/<!! c-out)))))

blak3mill3r01:02:57

a/onto-chan is just like doing this: (doseq [v values] (a/put! c-in v))

blak3mill3r01:02:07

the arguments to a/pipeline-blocking are: 1) number of threads concurrently doing the blocking operation 2) the "to" channel 3) a transducer 4) the "from" channel

blak3mill3r01:02:04

in this case it is a very simple transducer which, for each step, calls the blocking function passing that value as its one argument, and its return value will be put onto the "to" channel

blak3mill3r01:02:46

Does that help?

blak3mill3r01:02:59

the solution @U0P1MGUSX suggested might be better

blak3mill3r01:02:11

it's a small change to the code you pasted

lepistane07:02:06

@UC681SR17 > it's a small change to the code you pasted That's true but this is good opportunity to learn and use core.async 🙂 So if i understand correctly "to" channel will contain either the [:result v] in this case or exception in case long-blocking-op doesn't go as planned? In my case the ln context of writing to s3. It will either have success or error response which then i can handle by having listening to that "to" channel?

blak3mill3r16:02:39

No thread pool there, though, right? This code creates a thread for each message.

chrisblom16:02:09

it uses a cached thread pool

chrisblom16:02:05

So it reuses threads if possible

chrisblom16:02:40

That is an unbounded thread pool btw

chrisblom16:02:20

So it creates a new thread if none is available

hiredman17:02:08

The diff between in pipeline and this code is not the use of thread, but this code ignores the return value from uploading to s3, while pipeline expects it to be a useful value to passed down to some consumer

Claudio Ferreira17:02:54

Is clojure a frontend or backend language? Backend right? Clojurescript is frontend, but clojure is back given the fact that we can manage data/servers e etc in it, right?

dgb2317:02:16

if you think frontend as “running in the browser” then yes!

Mno20:02:13

Also clojurescript can run on the backend as well.. If you really wanted to for some reason.

blak3mill3r21:02:21

I mean there is really great support for using npm packages from clojurescript now, so if you for some reason wanted to access stuff on that platform, it might make sense to run clojurescript on the server

blak3mill3r21:02:05

Also, people have used Clojure in desktop applications as well, perhaps that is "front-end"

Alex Miller (Clojure team)17:02:15

Party in the front AND party in the back :)

😂 6
catjam 21
evocatus20:02:27

is there such a value that compared to a number (with >, <, >=, etc.) always returns false?

evocatus20:02:38

can I create one ?

noisesmith20:02:15

there might be some trick with reify, but I would guess you would run into issues with clojure's numeric optimization(?)

noisesmith20:02:22

I don't see anything promising on the Number class that would allow that https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Number.html

noisesmith20:02:05

@gr.evocatus I think ##NaN might actually be what you want

(ins)user=> (< ##NaN 0)
false
(cmd)user=> (> ##NaN 0)
false

👍 3
noisesmith20:02:01

it might even exceed your needs, as it isn't even equal to itself

noisesmith20:02:35

it's not a number, watcha gonna do? ¯\(ツ)

Mno20:02:57

That's amazing! I can't think of when I'd ever use this.. But it's amazing!

evocatus20:02:35

@noisesmith thanks! But I already did the bigger task in a different way

evocatus20:02:05

I'm writing my own binary heap implementation, just for fun and to learn Clojure deeper

andy.fingerhut22:02:24

If you need a total order of elements ##NaN is not what you want, since it isn't involved in any total order on the floating point numbers. It is an oddball value that has all of its own weird properites.

👍 3
Lukas20:02:31

Hey, technically not a clojure question but maybe someone is able to help: I use emacs/cider and have a clojurescript project with shadow cljs. I connected to the nrepl server but im not able to send s-expressions to the repl with C-c C-e like I do with clojure. Anyone know how to resolve this?

seancorfield20:02:09

@lukas.block If no one can answer here, maybe #cider and/or #shadow-cljs channels can help...

Lukas20:02:38

Thank you! I give it a try.