beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
West 2021-02-04T04:26:00.455200Z

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?

alexmiller 2021-02-04T04:29:45.455600Z

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

alexmiller 2021-02-04T04:30:15.455900Z

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

West 2021-02-04T04:34:32.456100Z

West 2021-02-04T04:35:15.456600Z

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

solf 2021-02-04T04:38:18.457100Z

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

solf 2021-02-04T04:38:30.457300Z

I think you want garden.core :as garden

1
seancorfield 2021-02-04T04:40:07.457600Z

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.

West 2021-02-04T04:41:00.457800Z

Ah man, that was so stupid.

West 2021-02-04T04:41:24.458Z

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

seancorfield 2021-02-04T04:42:17.458200Z

The editor put a warning wiggly line under garden/garden

seancorfield 2021-02-04T04:43:04.458500Z

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=>

seancorfield 2021-02-04T04:43:44.458700Z

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

alexmiller 2021-02-04T04:45:27.458900Z

_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_

seancorfield 2021-02-04T04:47:01.459200Z

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?

alexmiller 2021-02-04T04:47:14.459400Z

¯\(ツ)

alexmiller 2021-02-04T04:48:03.459600Z

I talked to the cider folks about this 2 years ago

alexmiller 2021-02-04T04:48:35.459800Z

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

alexmiller 2021-02-04T04:51:04.460Z

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

caumond 2021-02-04T05:52:03.460200Z

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

solf 2021-02-04T05:55:14.460400Z

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 @alexmiller is referring to this (unresolved) issue: https://github.com/clojure-emacs/cider/issues/2443

seancorfield 2021-02-04T06:12:16.460800Z

@caumond 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.

popeye 2021-02-04T12:26:48.462100Z

I have an atom object as below

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

popeye 2021-02-04T12:27:09.462700Z

How can I remove :b from atom?

2021-02-04T12:29:21.463700Z

(swap! my-atom dissoc :b)

borkdude 2021-02-04T12:29:34.464Z

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

popeye 2021-02-04T12:47:15.465Z

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

borkdude 2021-02-04T12:48:22.465500Z

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

popeye 2021-02-04T12:50:27.465700Z

its is not removing

borkdude 2021-02-04T12:51:46.466200Z

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

popeye 2021-02-04T12:54:22.466600Z

how to remove if it is a symbol?

popeye 2021-02-04T12:54:35.466900Z

in my code I have symbol

borkdude 2021-02-04T12:55:00.467300Z

replace :b with 'b

Marcus 2021-02-04T14:04:49.467900Z

which library would you use to access apache kafka?

R.A. Porter 2021-02-04T14:24:08.468100Z

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.

Marcus 2021-02-04T14:30:20.468500Z

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

Marcus 2021-02-04T14:31:22.468700Z

Why would you consider ketu instead of jackdaw?

R.A. Porter 2021-02-04T14:33:51.468900Z

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

R.A. Porter 2021-02-04T14:34:35.469200Z

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

Marcus 2021-02-04T14:36:14.469400Z

ok. thanks again!

Sebastian 2021-02-04T14:40:06.470500Z

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

Marcus 2021-02-04T14:45:27.470600Z

I'll test ketu and see how it goes 🙂

caumond 2021-02-04T14:49:26.470800Z

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

caumond 2021-02-04T14:50:28.471Z

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

lepistane 2021-02-04T14:58:22.471200Z

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

👍 1
Sam Bauch 2021-02-04T15:33:35.474400Z

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

javahippie 2021-02-04T15:34:40.474800Z

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

Sam Bauch 2021-02-04T15:35:04.475Z

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 Bauch 2021-02-04T15:35:45.475400Z

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 Bauch 2021-02-04T15:36:12.475600Z

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

javahippie 2021-02-04T15:36:47.475900Z

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

Sam Bauch 2021-02-04T15:37:02.476100Z

ok, that sounds good!

Sam Bauch 2021-02-04T15:37:54.476300Z

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 Bauch 2021-02-04T15:38:33.476500Z

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?

javahippie 2021-02-04T15:41:32.476700Z

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

javahippie 2021-02-04T15:42:00.476900Z

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

javahippie 2021-02-04T15:43:40.477100Z

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

javahippie 2021-02-04T15:44:57.477300Z

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

javahippie 2021-02-04T15:47:08.477900Z

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

javahippie 2021-02-04T15:48:15.478900Z

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.

lepistane 2021-02-04T15:49:48.480200Z

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/&lt;! 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?

blak3mill3r 2021-02-04T15:57:10.480700Z

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

blak3mill3r 2021-02-04T15:57:40.480900Z

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/

blak3mill3r 2021-02-04T16:01:20.482Z

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

blak3mill3r 2021-02-04T16:01:53.482700Z

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

blak3mill3r 2021-02-04T16:02:36.483500Z

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

blak3mill3r 2021-02-04T16:03:02.484100Z

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

javahippie 2021-02-04T16:03:12.484200Z

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

blak3mill3r 2021-02-04T16:03:24.484700Z

check out https://clojuredocs.org/clojure.core.async/pipeline-blocking

blak3mill3r 2021-02-04T16:05:00.485400Z

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

👍 1
Sam Bauch 2021-02-04T16:18:59.485600Z

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

Sam Bauch 2021-02-04T16:19:26.485800Z

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

👍 1
2021-02-04T16:27:55.489300Z

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/&lt;! in)]
        (async/&lt;! (async/thread (store-in-s3 msg)))))))

lepistane 2021-02-05T08:38:08.020Z

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

lepistane 2021-02-05T21:08:09.082100Z

@blak3mill3r @hiredman 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?

blak3mill3r 2021-02-06T01:21:20.090Z

@lepistane 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/&lt;!! c-out)))))

blak3mill3r 2021-02-06T01:22:57.090300Z

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

blak3mill3r 2021-02-06T01:24:07.090500Z

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

blak3mill3r 2021-02-06T01:25:04.090700Z

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

blak3mill3r 2021-02-06T01:25:46.090900Z

Does that help?

blak3mill3r 2021-02-06T01:27:59.091100Z

the solution @chrisblom suggested might be better

blak3mill3r 2021-02-06T01:28:11.091300Z

it's a small change to the code you pasted

lepistane 2021-02-06T07:49:06.132700Z

@blak3mill3r > 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?

blak3mill3r 2021-02-04T16:31:39.492800Z

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

2021-02-04T16:31:45.493Z

no

2021-02-04T16:32:09.493300Z

it uses a cached thread pool

2021-02-04T16:34:05.495300Z

So it reuses threads if possible

2021-02-04T16:38:40.496400Z

That is an unbounded thread pool btw

2021-02-04T16:39:20.497200Z

So it creates a new thread if none is available

Sam Bauch 2021-02-04T17:01:04.497800Z

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 😎

2021-02-04T17:04:28.498600Z

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

2021-02-04T17:04:53.498800Z

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

2021-02-04T17:08:30.499Z

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

Sam Bauch 2021-02-04T17:08:54.499200Z

thanks much for your help Tim!!

2021-02-04T17:10:39.499400Z

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

blak3mill3r 2021-02-04T17:42:49.499600Z

pipeline-blocking lets you control the concurrency explicitly as well

blak3mill3r 2021-02-04T17:43:11.499800Z

s/lets you/forces you to/

Claudio Ferreira 2021-02-04T17:53:54.001400Z

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?

alexmiller 2021-02-04T17:56:15.002200Z

Party in the front AND party in the back :)

7
😂 2
dgb23 2021-02-04T17:56:16.002300Z

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

javahippie 2021-02-04T18:28:37.003300Z

Happy to hear that! 🙂

evocatus 2021-02-04T20:11:27.004100Z

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

evocatus 2021-02-04T20:11:38.004400Z

can I create one ?

2021-02-04T20:13:15.005200Z

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

2021-02-04T20:14:22.005500Z

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

2021-02-04T20:15:05.006Z

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

(ins)user=&gt; (&lt; ##NaN 0)
false
(cmd)user=&gt; (&gt; ##NaN 0)
false

👍 1
2021-02-04T20:17:01.006400Z

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

Mno 2021-02-04T20:17:13.006500Z

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

2021-02-04T20:17:35.006700Z

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

Mno 2021-02-04T20:18:57.007900Z

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

evocatus 2021-02-04T20:19:35.008700Z

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

evocatus 2021-02-04T20:20:05.009300Z

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

Lukas 2021-02-04T20:52:31.013400Z

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?

seancorfield 2021-02-04T20:53:09.014Z

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

Lukas 2021-02-04T20:53:38.014300Z

Thank you! I give it a try.

blak3mill3r 2021-02-04T21:20:38.014400Z

people do

blak3mill3r 2021-02-04T21:21:21.014600Z

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

blak3mill3r 2021-02-04T21:22:05.014800Z

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

2021-02-04T22:26:24.015700Z

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.

👍 1