Fork me on GitHub
#beginners
<
2020-08-20
>
walterl00:08:17

If some-ns/some-method is a method of some protocol, how can I get its name ("some-method") from the some-ns/some-method var, from a different ns?

walterl00:08:32

nm. got it: (:name (meta #'some-ns/some-method))

walterl00:08:21

I tried (meta some-ns/some-method) (not the var)

hiredman01:08:53

You can call the .sym method on a car to get its name (a symbol)

walterl01:08:51

What's a "car"? :car:

walterl01:08:01

And thanks, I was unaware of .sym

hiredman02:08:31

Car is what slack on my phone insists on auto correcting var to no matter what I do

hiredman01:08:21

I think it is a non-namespace qualified symbol

hiredman01:08:48

There is another method to the namespace the var is interned in

seancorfield01:08:06

@clojurians-slack100 If you're on a recent version of Clojure: (name (symbol #'some-ns/some-method))

walterl01:08:37

Nice! That's new to me too. Is that preferred over (:name (meta ...))?

seancorfield01:08:32

I would consider it a more "sane" approach than going through the metadata.

seancorfield01:08:27

In Clojure 1.10 (or maybe since?), they made the whole name/`namespace`/`symbol` call space a lot more normalized... let me go find the commit...

seancorfield01:08:27

So, yeah, 1.10.

alexmiller01:08:30

Yes, preferred

hoopes02:08:50

hi, i’m back again - i’m using compojure-api, and i’d love to be able to use a plumatic schema to define my query params. it looks like all i can do is

:query-params [{q :- String ""}
                     {offset :- Long 0}
                     {limit :- Long 10}]
and explicitly specify each query param. Is that true? Is there an easy way to use a schema here to coerce my query params (and work with swagger ui)? Every combination of :query or :query-params that is not this exact type of incantation gives me errors in the eg GET macro. Fuller example of what i’m doing is something like…
(GET "/search" []
        :return SearchReponseScheam
        :query-params [{q :- String ""}
                       {offset :- Long 0}
                       {limit :- Long 10}]
        search-handler-fn)
Thanks in advance for any help. It seems like just defining the routes and parameters has been the hardest part of building a clojure rest api so far 🙂

seancorfield03:08:52

Since you have the same code (that apparently works?) in both code fragments, can you share what you tried that didn't work?

seancorfield03:08:13

Or am I misunderstanding what you're saying / trying to do?

hoopes23:08:41

:query SearchParamSchema

Exception in thread "main" Unexpected error macroexpanding GET at (the/routes/file.clj)
…snip…
Caused by: java.lang.UnsupportedOperationException: nth not supported on this type: Symbol
:query [SearchParamSchema]
Exception in thread "main" Unexpected error macroexpanding GET at (the/routes/file.clj)
…snip…
Caused by: clojure.lang.ExceptionInfo: Call to clojure.core/let did not conform to spec.
:query-params SearchParamSchema
Exception in thread "main" Unexpected error macroexpanding GET at (the/routes/file.clj)
…snip…
Caused by: java.lang.UnsupportedOperationException: count not supported on this type: Symbol
:query-params [SearchParamSchema] This let the server start, but the single param (in swagger) was SearchParamSchema as a string. Maybe I could unpack the schema somehow here? https://github.com/plumatic/plumbing/tree/master/src/plumbing/fnk#fnk-syntax This link to syntax seems to come up, but I’m not exactly sure how that applies to what I’m trying.

Trung Dinh02:08:37

hi, not sure how to understand ^String (with that hat) in this form (String. (.decode (Base64/getDecoder) ^String to-decode)) ? thanks

andy.fingerhut02:08:26

That is a type hint. It indicates that to-decode should be a JVM object of type java.lang.String. The Clojure compiler can use that to decide which String constructor to call, if there are multiple methods with the same number of arguments, but different types, in that parameter position.

ian_sinn04:08:17

I think I'm missing something basic. is there a reason "dot methods" (i.e. java-interop methods, not sure what you call them) cannot be passed around? Namely, I ran some code like this. The items in coll have a .text method :

(map .text coll) ;; Unable to resolve symbol...
(map #(.text %) coll) ;; Works! 

ian_sinn05:08:38

Ah, thank you! I been looking at a much less in-depth page which I thought was the source of truth https://clojure.org/guides/learn/functions#_java_interop

noisesmith09:08:51

to be pedantic about terminology: methods cannot be passed around period, they are properties of objects clojure functions are objects with an invoke method

noisesmith09:08:24

some languages use method / function loosely or informally, in clojure it's a true difference

noisesmith09:08:22

if java had usable first class methods I suspect clojure wouldn't have needed to be invented

ian_sinn09:08:18

Thanks @U051SS2EU the clarification is helpful. I'm coming from JS where there isn't a real method/fn distinction

noisesmith09:08:18

right - it's inverse! there's no true methods in js, just functions owned by objects, this was intentionally borrowed from how lisps do it

noisesmith09:08:57

then there's machine code, where there's no objects or functions, just bytes

noisesmith09:08:33

:mind blown emoji missing:

ian_sinn09:08:08

oh didn't know that's where that part of jS got it's roots. I'm definitely feeling rather at home in CLJ with functions everywhere, except that is until I start talking to java

noisesmith09:08:33

yeah, hopefully the history helps make it more meaningful

seancorfield05:08:17

@ozzloy_clojurians_net Namespaced keys in a map.

ozzloy05:08:18

what's that construct called?

seancorfield05:08:12

It's a shorthand for {example.biff.auth/send-email send-email example.biff.auth/on-signup "/signin/sent" ...}

Henry09:08:14

Is "brew install" the only way of installing Clojure on MacOS? Wondering if there is any alternative way that does not involve using homebrew. Thanks.

andy.fingerhut13:08:11

If you are using the Clojure CLI tools from http://clojure.org, then I do not know any way to install those without first installing homebrew.

andy.fingerhut13:08:47

You can certainly use Clojure without the Clojure CLI tools, e.g. some people use Leiningen for Clojure development, and that can be installed without installing homebrew.

andy.fingerhut13:08:26

Technically you can go very bare-bones and use Clojure by copying Java JAR files around by yourself, but I don't know anyone who wants to do that.

Henry14:08:36

Thanks Andy for your help. Have been using Clojure in a Linux virtual machine since I would rather not have brew change permission for /usr/local. However, virtual machine does run a little sluggish, so I am hoping to switch back to MacOS for the long run. The risk is minimal and probably a non-issue for most programmers, but it would be excellent if there is a way to install Clojure CLI tools on MacOS without using brew.

andy.fingerhut15:08:43

What risk are you referring to? I thought that Homebrew was designed to use super user privs only at initial install, and then not need them for later changes to brew packages

practicalli15:08:19

Have you tried using the Unix script on MacOSX, it's just an installer that puts files in the right places I believe. Would be surprised if it didn't work on Mac

practicalli15:08:56

Or you can just copy what that Unix script does

Henry17:08:14

@U0CMVHBL2 Read about this quite some time ago from a website. The website seems to be gone. Found a web archive link. Check this out (with a pinch of salt though as it's quite hypothetical): https://web.archive.org/web/20190925064628/https://applehelpwriter.com/2018/03/21/how-homebrew-invites-users-to-get-pwned/

Henry17:08:19

@U05254DQM Good idea. I'll give it a try. Thanks for helping!

practicalli18:08:41

@UVDMR4Y75 I would be very interested in knowing how you get on. I’d like to add that approach to my install info if it works. Thank you. https://practicalli.github.io/clojure/clojure-tools/install/install-clojure.html#clojure-cli-tools

Henry08:08:31

@U05254DQM It'd be my pleasure. Still have not figured out a way yet. Using Unix script should work, but would come full circle because MacOS does not come with the APT command and requires Homebrew or MacPorts etc to install it. MacPorts looks nice, but for the time being, I'll just stick with my Linux machine. By the way, your website is awesome and an incredible source of Clojure knowledge for the community. Thanks a lot for your contributions!

heyarne10:08:27

I'm trying to parse a http response that is separated by a multipart-boundary; essentially i want to get the first part and discard the rest. I thought an elegant approach would be this: get the response body as a reader, read it lazlily into a byte sequence, split that whenever I see the boundary, and take the first result of that split. I wrote this to treat an InputStreamReader as a lazy sequence but it seems to try and read the entire reader eagerly. Why?

(defn input->byte-seq [input]
  (lazy-seq (cons (.read input) (input->byte-seq input))))

noisesmith10:08:31

if this isn't a learning exercise, there's a ring middleware for multipart

noisesmith10:08:43

@arne-clojurians in what context is the reader eagerly being consumed? your repl? application code?

heyarne11:08:06

You had a good instinct here! I tried to debug it in Cider with a #dbg statement and the step debugger tried to realize the whole sequence (i think).

noisesmith10:08:55

this behavior looks correct:

org.noisesmith.hammurabi.hold-my-beer-test=> (def sr (java.io.StringReader. "abcde"))
#'org.noisesmith.hammurabi.hold-my-beer-test/sr
org.noisesmith.hammurabi.hold-my-beer-test=> (def ibs (input->byte-seq sr))
#'org.noisesmith.hammurabi.hold-my-beer-test/ibs
org.noisesmith.hammurabi.hold-my-beer-test=> (.read sr)
97
org.noisesmith.hammurabi.hold-my-beer-test=> (char *1)
\a
org.noisesmith.hammurabi.hold-my-beer-test=> (first ibs)
98
org.noisesmith.hammurabi.hold-my-beer-test=> (char *1)
\b

noisesmith10:08:05

if you just call input->byte-seq in a repl, without wrapping it to avoid printing, the print behavior will force the byte seq, even though only an opaque identity is printed

noisesmith10:08:02

also btw your input->byte-seq needs a test for end of stream, it's sitting here producing a massive lazy seq right now and I am waiting for it to crash with an OOM

org.noisesmith.hammurabi.hold-my-beer-test=> (def sr (java.io.StringReader. "abcde"))
#'org.noisesmith.hammurabi.hold-my-beer-test/sr
org.noisesmith.hammurabi.hold-my-beer-test=> (input->byte-seq sr)

noisesmith10:08:58

it can't print the object identity for the lazy-seq without reading the entire seq (this relates to hash identity and the print form using that identity iirc, and the hash requires full realization)

ashnur10:08:38

I am getting these warnings

113 |         (kp/let [element (.$ page "#asset-type-select-options")] 
--------------------------------^-----------------------------------------------
 Cannot infer target type in expression (. page $ "#asset-type-select-options")
I saw this https://clojurescript.org/guides/externs#externs-inference but I don't see how it would help here. I am not even sure what 'target' means in that sentence. I am also not sure how I could explain to it the type since I don't know it myself, page is coming from puppeteer and the whole thing is promise based.

kwrooijen11:08:49

Probably better to ask in #clojurescript . But you could try this: (.$ ^js page "#asset-type-select-options")

ashnur11:08:21

thanks, that's a good pointer

ashnur12:08:56

https://shadow-cljs.github.io/docs/UsersGuide.html#_simplified_externs so weird because I read this a couple of times and it didn't register at all, I thought it was about something else

ashnur12:08:07

Now I have a file and the warnings are all gone

Shuai Lin02:08:43

Take a look at applied-science/js-interop lib. I find it very useful in cases

bigos16:08:43

How do I use global variables in Clojure? I want to update the global variable in one function and read the updated value in another. I am also familiar with Elm and passing model around to achieve the same. But I can't figure out how to do it in Clojure. Googling gives me lots of nonsese that does not make sense.

dpsutton16:08:11

often (not always) the answer is don't use global variables. but there are several ways to use atoms and functions to achieve the result you want. Can you go into what your use case is? Many times global state falls away. But a straight forward way to do this is (def foo (atom 1)) and anyone can call (reset! foo 2) and get the value with @foo.

SoV416:08:51

yeah, atoms are what you want

dpsutton17:08:46

atoms are many times what you want. especially when new to the language it is easy to try to mimic other languages and use mutable containers where they are not needed.

bigos17:08:42

@dpsutton I worked straight away! But why when I google for this thing there is no example to be found?

dpsutton17:08:58

an example, in other languages to sum numbers in a list you might do something like

(def total (atom 0))
(doseq [x [1 2 3]]
  (swap! total + x))
but this is extremely far from what it would look like in Clojure

bigos17:08:04

google was directing me to bind and set!

dpsutton17:08:32

can you go into what you are trying to do?

seancorfield17:08:41

That's because "global variable" means something different in Clojure to other languages @ruby.object

bigos17:08:56

to get my foot in the door in need to use some old methods and tinker from there

bigos17:08:23

@dpsutton confirmed, you advice has worked

seancorfield17:08:34

You'll find that trying to write Clojure in an imperative style with "variables" will be hard and will also not be idiomatic.

NoahTheDuke19:08:54

as someone working on a highly imperative codebase written in clojure, don't do it, it's hell

bigos17:08:35

@seancorfield I see your point

bigos17:08:13

but please see my side of the story, i need imperative training wheels

seancorfield17:08:57

I think you'd do better in your Clojure journey by just abandoning that thinking and pretending you're learning programming from scratch.

alexmiller17:08:39

1 step backward, 900 steps forward

bigos17:08:40

@seancorfield my current attmpts were very frustrating, so I have nothing to lose

jaihindhreddy19:08:03

I remember my first functional programming language being very hard to learn as well (It was Elm). Please be kind to yourself, and give it some time.

practicalli05:08:44

You may find this free book helpful https://practicalli.github.io/clojure-webapps/ There is still lots to add, but should have some useful info for you

Lu17:08:46

Not sure if it applies to your case, but using - let - might be already a better solution if your functions are invoked sequentially

dpsutton17:08:14

if you discuss what you're trying to do perhaps we can talk about different approaches and how to think about it. its possible atoms are what you need, its possible they are bad for this use case. But would love to know what you're doing if you want to share

bigos17:08:38

(ns server.start (:require [ring.adapter.jetty :refer [run-jetty]])) (def x (atom "1")) (defn handler [request] {:status 200 :headers {"Content-Type" "text/html"} :body (str "Hello World " @x)}) (defn -main [& args] (println (str "task for starting server on port 3000 with args" args)) (reset! x (str args)) (run-jetty handler {:port 3000 :join? false}))

seancorfield17:08:37

FYI, you can use triple backticks around blocks of code to make it easier to read:

(ns server.start
  (:require [ring.adapter.jetty :refer [run-jetty]]))

(def x (atom "1"))

(defn handler [request]
  {:status 200
   :headers {"Content-Type" "text/html"}
   :body (str "Hello World "  @x)})

(defn -main [& args]
  (println (str "task for starting server on port 3000 with args" args))
  (reset! x (str args))
  (run-jetty handler {:port 3000 :join? false}))

seancorfield17:08:34

(you may need to change a Slack preference so enter inside code blocks adds a newline instead of sending the message -- I can't remember what the default is these days)

bigos17:08:10

can i wrap let accross those functions?

bigos17:08:10

can i wrap let accross those functions?

Lu17:08:55

Nope but you can simply pass the args down as second argument of handler :) the other guys are much more experienced so they’ll tell you better

seancorfield17:08:22

As a data point, at work we have over 100,000 lines of Clojure code and we only have 85 atoms (and no refs).

alexmiller17:08:41

even that seems high :)

seancorfield17:08:01

Agreed. I'm sure we can get rid of a lot of those.

seancorfield17:08:50

@ruby.object the typical Clojure approach is to use middleware or a closure to get parameters into a function like that:

(defn make-handler [args]
  (fn [request]
    {:status 200 :body (str "Hello World " args)}))

(defn -main [& args]
  (run-jetty (make-handler args) {:port 3000 :join? false}))

seancorfield17:08:40

(make-handler args) returns a function that "closes over" the arguments.

Zebra20:08:51

Hello everyone! How do i stop myself from structuring my apps as if i had actors? I've use erlang/elixir and got very used to genservers and dynamic supervisors, so i would greatly appreciate any tips or links to books or videos.

phronmophobic20:08:09

what's wrong with actors?

phronmophobic20:08:41

the jvm doesn't give you all the benefits of using actors as the erlang vm, but many of the same principles can still be reused

phronmophobic21:08:14

what kinds of projects are interested in?

Zebra21:08:32

@U7RJTCH6J for example i've dabbled with core.async and dynamically created components that subscribe to a chan and do the work and send it downstream. About projects, for example i did data ingestion pipeline with elixir that was broadcasting partitioned data to users based on their subscriptions. Basically i like soft/hard realtime projects involving data processing

phronmophobic21:08:16

those sound like projects that fit well with actor based designs. although core.async isn't technically an actor based system, it should feel familiar. I guess the big difference with clojure is that you have the option of using shared state with things like atom and more ,but it's totally acceptable to connect separate processes/threads together via queues

phronmophobic21:08:20

book recommendations comes up frequently. it would probably great to have a pinned post for something like that. here's a more comprehensive list: https://clojure.org/community/books

Zebra21:08:01

@U7RJTCH6J so im just dealing with projects that are better suited for actor model? I've read programming clojure 3rd edition, elements of clojure and hands on reactive programming with clojure,but i still feel like i dont know anything and got by just fine just because i got lucky with otp.

phronmophobic21:08:19

i wouldn't necessarily say the actor model specifically. I was thinking about CSP style designs (of which the actor model is one). the main caveat being that with actors, the processes and their mailboxes are combined whereas with CSP, you also have the option of having queues available separately from the process

phronmophobic21:08:47

there's a #code-reviews channel. it might be helpful to post some code and see what others think

phronmophobic21:08:09

if you haven't checked out all of the Rich Hickey talks, I would definitely recommend those, https://changelog.com/posts/rich-hickeys-greatest-hits. on this topic, I would specifically recommend "the language of the system" and "the value of values".

Zebra21:08:46

@U7RJTCH6J thanks!

phronmophobic21:08:32

in fact, the core async talk might be the best talk to get started with on this topic

phronmophobic21:08:11

generally, connecting isolated processes via queues is great. I recommend the joe armstrong erlang paper to clojure programmers all the time, http://erlang.org/download/armstrong_thesis_2003.pdf

Andrew Gracey21:08:54

This seems super simple but I can't find it documented anywhere. How do I use lumo to build a reagent application? All of the examples for reagent use a project.clj instead of build.cljs and I don't understand the mapping from one to the other

Michael Stokley22:08:22

anyone familiar with scope capture? i'm trying to programmatically sift through hundreds of ep-ids. my first attempt is (doseq [ep-id (range 70 300)] (sc.api/defsc [ep-id -8])), which throws ep-id should be either a positive number or a [(positive-number) (negative-number)] tuple, got: [ep-id -8]. anyone know what i might be missing, here?

Michael Stokley22:08:56

part of the answer is that sc.api/defsc is a macro, but that's as far as i get.

alpox22:08:12

Im a beginner myself so I dont know how to deal with it, but id say defsc is a macro taking ep-id as a symbol rather than the number the symbol refers to

alpox22:08:18

The only way around that I know is using another macro on top rather than a function