Hi,
lets say I have a module that is designed to handle some data manipulation for a model.
I need to have two getter functions, one not safe, if key is missing throws exception, another safe, if key is missing returns nil
(get model attr) ;; => throws if attr is not in the model
(get-safe model attr) ;; => returns nil if attr is not in the model
The logic is that attr
in 99% cases should be in the model
and if it's not it's an error, second function is for 1% cases for which is ok to no have attr
My question is it's creating two separate functions a good approach, if yes is there any naming convention for such case?
Having two separate functions seems reasonable.
get-safe
appears to be the same as clojure.core/get
so I would just use get
.
for the version that throws, I would consider some other short names, but try to make sure they are distinct from get
so that it's easy to distinguish safe vs unsafe:
• get!
• grab
• must
• pick
• nab
• net
• clutch
Hey guys, does someone know how to copy the whole block of code I've just written using rebel-readline
? Thanks for the help 🙂
You entered the code directly into the repl? You want to not do that. The repl should be the target where the code is evaluated but you should be typing your code into an editor hooked up to your repl.
I understand but for simple use cases I wanted to use the REPL directly, if it's not possible when I will use an editor but seem overkill to me
Suggest using the command history. Use the up arrow to go back through the history and down arrow to go forward through history
If you want to copy the code from the terminal into another application, then select it with a mouse 🤷
Ah, Ctrl-U
will kill the line and should put it into the clipboard / kill ring
but that probably only works if the code is on the same line...
Thanks for the insights ! The problem is that if your input is multi-line this does not work because either rebel add #_
prefix and Ctrl U only kill one line
So yes I think I will use an editor seem easier haha
I start rebel readline in the terminal along with an nREPL server, so I can connect to that repl process from Clojure editors
Thanks a lot seems really good, will try that
If you are using Clojure CLI tools to run the REPL, I have example aliases for running rebel and nrepl together https://github.com/practicalli/clojure-deps-edn#repl-terminal-ui
For those that had to talk to SOAP service, how did you go about approaching it from Clojure?
When I use sh
with escaped characters it turns them into literals:
(sh "bash" "-c" "bwa mem -R '@RG\tID:A\tSM:A' hg19.fasta | samtools view -Sb - > result.delteme")
gives the error:
[E::bwa_set_rg] the read group line contained literal <tab> characters -- replace with escaped tabs: \\t
https://github.com/xapix-io/paos You could try this library.
However not any wsdl can be processed by that lib.
We used wsdl2java
to produce Java source classes, and compiled them into a library, which we used directly from Clojure via interop — and then plain HTTP to talk to the SOAP service with the XML those classes produced.
(it was a while back and, luckily, we don’t have to talk to any SOAP services now)
those are literal tabs in your clojure strings
if you want to have SLASH-T, you need to escape the slash in your literal:
"\\t"
"\t" is clojure syntax for a string containing only tab character, aka ascii/unicode 9
"\\t" is a two character string, slash followed by t
Understood. So there is nothing like pythons r"\t"
(where r denotes a raw string)?
no
if you want a raw string, you can read a file
via slurp
, just not in situ in source code
Can someone explain to me how exactly specs
or malli
"replaces" types? I see that they're good descriptive frameworks for specifying, validating, and conforming data but outside of generated tests and markdown documentation (e.g. autodoc
can use specs), I don't see how they replace types. I suppose it offers alternatives to types (just use unit tests if you want safety as you develop), but maybe I'm missing something.
they don't replace types
@seancorfield yep, looks like the "simplest" approach
@delaguardo yeah, need wsdl processing
x "replaces" y is a big (maybe unnecessarily) assertion, so I'm going to set that aside for a second. there's a short but dense paragraph in the rationale doc that offers some insight into the contrast of spec / types https://clojure.org/about/spec#_expressivity_proof
I made a mistake in my sentence) corrected - not every wsdl can be processed. So you can try yours )
Oh ok, will take a look, thx
One thing I'm starting to see is the use of these DSLs
for creating validation "functions". That way you can have sanitizers around your functions without introducing "types" that just result in runtime errors (as opposed to here where a wrong "type" could result in anything you want: an error, a default value, etc.
;; Spec
(s/def :clean-event/user (s/keys ...))
(s/def :raw-event/user (s/nilable :clean-event/user))
(s/def :event/cleaned? boolean?)
(defmulti event-cleaned? :event/cleaned?)
(defmethod event-cleaned? false [_]
(s/keys :req [:event/cleaned? :raw-event/user]))
(defmethod event-cleaned? true [_]
(s/keys :req [:event/cleaned?] :opt [:clean-event/user]))
(s/def :event/event (s/multi-spec event-cleaned? :event/cleaned?))
;; Code cleanup
(defn cleanup [{:raw-event/keys [user] :as event}]
(-> event
(dissoc :raw-event/user)
(assoc :event/cleaned? true)
(assoc-unless-nil
:clean-event/user user)))
Is this very common? For me, this seems like a more flexible alternative to appending ::pre
and ::post
to all my functions.
tools like clj-kondo can push the spec/malli function validation to static analysis phase, but they are still not types. Would love to see more developer tooling for these. Intellisense/autocompleting based on specs/schemas etc.
just looked at typehole, a VSCode plugin for creating TypeScript definitions from sample data. Wound be easy to do with Clojure.
didn't you already support something like this with malli?
yes, but that could be integrated into editors.
also malli->TypeScript might be useful.
This code works fine in terms of doing the job, but I wonder if there is a more terse route to the same goal? Also, it’s not really clear to me when it is necessary to use the # before a function in these kinds of configurations. I realise that makes it an anonymous function, but I don’t yet understand when this is called for. (def nos (take 20 (repeatedly #(+ 24 (rand-int 60)))))
a clarification that might help, #
never goes before a function. It is a reader macro that creates a function out of the following form
#(inc %)
is just shorthand for (fn [x] (inc x))
. Steer clear of it until that makes more sense if you like. When you need a function just start with (fn [...] ...)
@rherrema I'm not sure I understood your question - is it about when you need a function vs. just inline code, or about the anonymous function syntax itself?
the code works fine... but you want it terser?
i would expect the opposite, ie although the code may work fine, let's make it more expressive
@dpsutton Thanks - that’s helpful.
@noisesmith what I’m wondering is why I can’t simply write (+ 24 (rand-int 60))
in that location and instead need to write #(+ 24 (rand-int 60))
repeatedly
wants a function. (+ 24 (rand-int 60))
is a number, not a function.
@michael740 that is an interesting perspective - I suppose I am accustomed to working in DSL’s where there’s an attempt to create as direct and efficient a mapping as possible between concept and the expression in code - ‘expressive’ can be construed in multiple ways. As a composer who does live coding of music, using terse but clear forms of code is super valuable.
@coyotesqrl that’s very clear - thanks
if you want it more expressive, you can name the random function (defn random-character [] (+ 24 (rand-int 60))
so that your expression where you use it reads a bit more human friendly: (take 20 (repeatedly random-character))
or whatever domain object you are using
depends on what you think expressive is. people might consider Clojure expressive since the code to do this is so concise and has built-ins that are ready for it: (take 20 (repeatedly #(+ 24 (rand-int 60))))
or might think more human readable, mapping onto the domain is expressive. in addition to lots of other conceptions of expressive that might be out there
Is there something akin to group-by
(without the grouping, rather acting like repeated applications of assoc
) that could do something like
(reduce (fn [m things] (assoc m (:name thing) thing)) {} things)
In a more concise way? In other words, make a map by keying off a particular attribute? Seems like a super common thing to do, but I didn't spot anything handy for itcan you give a representative input and output? having a hard time conceiving what is expected
@dpsutton I like your idea of naming the random function (I would call it random-note, as I’m in the domain of music). And yes, ‘expressive’ is conceived in many ways in various domains. That’s why, coming from music, I find it fascinating to hear it used in the context of programming.
In: [{:name "Bob" :job "Janitor"} {:name "Jill" :job "Accountant"}]
Out: {"Bob" {:name "Bob" :job "Janitor"} "Jill" {:name "Jill" :job "Accountant"}}
In this case, the names could expected to be unique...more like the attribute is :id
^^^ @dpsutton
(into {} (map (juxt :name identity) coll)
is a common idiom for this.
Oh...that's helpful. I think that's definitely nicer. Thanks @dpsutton
or just get used to using group-by, which works for non-primary keys as well
see also index-by
https://weavejester.github.io/medley/medley.core.html#var-index-by
because repeatedly requires an argument that can be called, (+ 24 (rand-int 60))
simplifies to a number, calculated once and reused (try this with repeat). the #() syntax creates a function that executes the body again each time you call it
so instead of asking clojure to calculate a number, you are asking it to create a special kind of object (a function) which instead of doing the work now, does it later when asked for (and redoes the task each time, which is useful with side affects like randomness)
if repeatedly was a macro it could create the function for you, but it's much more useful for it to be a function taking another function
(defmacro rp
[& body]
`(let [f# (fn f# []
(lazy-seq (cons (do ~@body)
(f#))))]
(f#)))
user=> (take 10 (rp (rand-int 10)))
(7 4 8 3 4 3 8 1 3 1)
this is less flexible than repeatedly, it can't be used as a building block without making another macro (or using it inside a macro), unlike functions which can be used as values