This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-11-23
Channels
- # aws (2)
- # beginners (57)
- # boot (63)
- # cider (7)
- # clara (1)
- # cljs-dev (1)
- # cljsrn (5)
- # clojure (68)
- # clojure-brasil (1)
- # clojure-dusseldorf (2)
- # clojure-greece (10)
- # clojure-italy (29)
- # clojure-russia (1)
- # clojure-spec (9)
- # clojure-uk (66)
- # clojurescript (16)
- # cursive (18)
- # datomic (19)
- # docker (3)
- # figwheel (2)
- # fulcro (61)
- # instaparse (7)
- # jobs (1)
- # luminus (5)
- # lumo (47)
- # mount (6)
- # off-topic (13)
- # onyx (39)
- # planck (4)
- # portkey (2)
- # re-frame (28)
- # ring (6)
- # ring-swagger (30)
- # rum (3)
- # shadow-cljs (142)
- # spacemacs (5)
- # sql (2)
- # unrepl (61)
- # untangled (2)
Hello everyone, I've just recently started with clojure. Coming from C-like programming languages (I have some experiences in Java and Go), I've gotten used to do some kind of early return function like:
if userNotFound {
return ErrorNotFound
}
doSomethingElse()
if somethingElseHappened {
return UnexpectedError
}
return nil
I mainly used it when writing web services to determine what kind of response would I want to return to the caller. For example, if the return value is some kind of error or exceptions, I would return the status code appropriate to the type of error, usually the case of 4xx vs 500.
When using clojure I understand I can't really write it like that anymore so the most that I can came with is something like this:
(defn signup
[credential]
(let [user (find-by-email (:email credential))]
(or
(invalid-registration? credential)
(user-already-exists? user)
(register credential))))
I can't help but feel that there should be more idiomatic way to write this kind of functionality. Is there another way to do this kind of function or is this acceptable?@hawari.rahman17 There are various ways to handle this sort of thing. If you have predicates that return an error code or nil, using or
is fine, but it's probably more idiomatic to use cond
:
(defn signup
[credential]
(let [user (find-by-email (:email credential))]
(cond (invalid-registration? credential)
:invalid-registration
(user-already-exists? user)
:user-exists
:else
(register credential))))
and then your predicates would just return true/false.This would turn the predicates into "pure" predicates (`?` usually means Boolean -- just true or false), and decouples the error codes from the predicates themselves.
Ah I see, seems like I have to rewrite it then. As of now my invalid-registration?
or other "validation" function returns a vector as the truthy
value, containing the error type and the message itself:
(defn invalid-registration?
[credential]
(if (invalid-credential? credential)
[:bad-request (compose-error (credential-errors credential))]
false))
What do you suggest if I had to return such value @seancorfield?I was kinda hoping of something akin of a flow, where if there's something happened on any part of the flow, don't proceed to another step.
If something is a predicate -- with ?
-- then it should just return true or false. The error code/message is a control flow function and belongs in the "controller" logic, not the predicate. Otherwise you're conflating a test with how it should be exposed to end users.
One of the "themes" of Clojure is keeping things separate and uncoupled -- uncomplected. @hawari.rahman17
Alright, I think I get the idea now, do you mean something like:
(defn signup
[credential]
(let [user (find-by-email (:email credential))]
(cond (invalid-registration? credential)
(do-something-if-invalid)
(user-already-exists? user)
(do-something-if-already-exist)
:else
(register credential))))
That separates the decisions from the actions, which is a good way to go.
Thanks @seancorfield, I think I quite understand now. Such a concise and clear explanation.
Cool. And "Welcome to Clojure!" 🙂
@hawari.rahman17 I started with Clojure in 2010 and we've had it in production since 2011. Very happy with that decision!
Hi, when I add the period "."
to the last line of the books->string
function I get the print of LazySeq, an don't understand why.
First without the "."
:
(defn books->string [books]
(cond
(= 0 (count books)) (str "No books.")
(= 1 (count books)) (str "1 book. " (apply book->string books) ".")
:else (let [each-book
(fn [book] (book->string book))]
(apply str (count books) " books. " (interpose ". " (map each-book books) ) ))))
(books->string [little-schemer, cities, wild-seed])
"3 books. The Little Schemer, written by Daniel Friedman (1944 - ), Matthias Felleisen. The City and the City, written by China Miéville (1972 - ). Wild Seed, written by Octavia E. Butler (1947 - 2006)"
but I need the "."
at the end of the text. Thought that this would do it as the last line of the defn
:
(apply str (count books) " books. " (interpose ". " (map each-book books) ) "."))))
but returns "3 books. [email protected]."
. Why this since apply str
should (in my view) apply to all arguments?@lum apply only "unwraps" the last arg
so you get the str of a lazy-seq (totally useless) then each element of "." - a single char
you can fix this by having an extra call to str wrapping the whole thing, or by using (concat (map each-book books) [""])
or using (interleave (map each-book books) (repeat "."))
(instead of interpose)
thank you very much @noisesmith. Didn't know that it'd unwrap only the lat argument. I found another way, after knowing that: (str (count books) " books. " (apply str(interpose ". " (map each-book books) )) "." )
right, my first option "extra call to str wrapping the whole thing"
ah, ok, I understood a new str
would come first. Sorry, I'm not native english speaker so don't get right some meanings.
:thumbsup:
@lum here's a comparison of interpose
and interleave
+user=> (interpose "." [:a :b :c])
(:a "." :b "." :c)
+user=> (interleave [:a :b :c] (repeat "."))
(:a "." :b "." :c ".")
now that I think about it, clojure uses some very obscure english words 😄
I'm doing this to the
clojure course, and didn't "learn" interleave
yet, so can't apply, but it's good to know. 🙂
another way to get that would be (mapcat #(vector % ".") [:a :b :c])
@alexkeyes consider that you have some conditional deciding what function gets passed to a higher order function
sometimes (constantly true) is the most straightforward way to describe what you want
Is there a better way of chaining maps than : (->> (range 36) (map index-to-position) (map cells-around-position) (map count))
?
@dromar56 you could do (->> (range 36) (map (comp count cells-around-position index-to-position)))
@noisesmith thanks for the concise explanation!
Interesting, thanks @sundarj. I still have some trouble with comp
and its (seemingly) reversal of order
@dromar56 it follows the order you would write the functions manually: ((comp f g h) x)
=> (f (g (h x)))
@dromar56 you can use transducers with a small change to your syntax (into [] (comp (map index-to-position) (map cells-around-position) (map count)) (range 36))
the funky thing is that transducers compose functions rather than composing values from the functions, so the order goes backward to normal comp
i don't think this is a clojure specific question but it comes up a lot when i'm writing clojure. 🙂 i have a clojar dependency with a CSS file inside the jar. how can i reference that css file within my parent clojure project / scss build tool?
Can someone recommend a resource on how to deal with input validation in a clean way without exceptions? Are Monads the only option? Or is there another way?
@vinai input validation in... the browser? on the command-line? what are you getting into?
@vinai how many different inputs are you working with? if you need to make hundreds over the lifetime of the app it may be worthwhile to invest some time in solving the general case. my treatment of this on the web side is with atoms that hold the values of the inputs and are validated on submission action... since it's an api request you can't really limit the input fields as you would an input box, but you can definitely deconstruct the input via compojure in your router (server.clj) and make a handful of custom methods that will "validate" inputs, returning some sort of general case "hey that dint work my man" and perhaps the list of specific inaccuracies. so you can haeve a method to validate dates, to validate integers, phone numbers, whatever.. but i think you may need a custom validation method for each kind of input you're working with. are they really abstract bodies of data?
in terms of libraries, plumatic/schema has code designed for validating client data https://github.com/plumatic/schema
@sova why use an atom?
@noisesmith my mind is in web dev mode 😄 as far as i am aware, the only convenient way to persist values in something like client.cljs is with an atom, and you can then use the atom references when you wish to submit / send stuff over the wire.