Fork me on GitHub
#beginners
<
2019-04-26
>
peterhull9007:04:49

What's the neatest way to look for keys in a hashmap in order, running a particular function on the first one found. e.g. I've got a thing with an optional :display-name key, optional :id key and further keys. If it has a display name I want to format that, otherwise if it has an id I want to format that in a different way, otherwise ... and so on. I can use a nested if-let but it's a bit messy and condp doesn't quite work.

orestis07:04:39

Plain cond should do it, no?

orestis08:04:13

(defn sel [x]
  (cond 
    (:a x) (f x)
    (:b x) (g x)
    :else "nothing"))

peterhull9008:04:13

OK, but ideally I'd want f to take an arg of whatever (:a x) is

orestis08:04:55

edited πŸ™‚

peterhull9008:04:55

(cond
 (:a x) (f (:a x))
 (:b x) (g (:b x))
 :else "nothing")

peterhull9008:04:02

without repeating the get

peterhull9008:04:21

obviously that's fine but I always suspect there's a neater way in clojure.

orestis08:04:03

There have been discussions around better-cond some time ago, it seems a lot of Clojure shops are building some flavor of their own

orestis08:04:37

This is a nice exercise for writing your own macro, I think. Mostly to expand something like

(foo x
  :a f
  :b g
  "nothing")
to that cond above πŸ™‚

orestis08:04:53

Now, coming up with a name for that foo is the truly hard part.

steven.katz19:04:36

Nested (if-let)

peterhull9008:04:32

better-cond might do it, looks maybe a sledgehammer to crack a nut! I'll have a go at a macro, it's good practice anyway

peterhull9008:04:40

Thanks orestis

orestis08:04:26

Yeah I get it β€” but sometimes adding a library like that to your dependencies early on saves you for having to judge cost/benefit for every little bit tiny use case.

orestis08:04:54

If you have it, might as well use it in many places, I mean.

rob.rodz.jr908:04:51

hey, looking to make friends. dont have any

grierson10:04:39

Hey, does anyone have any other video/blogs that talk about Clojure at scale? like this https://www.youtube.com/watch?v=av9Xi6CNqq4 or Rich Hickey's Language of the System.

ballpark14:04:03

Good question. I would also like to hear more about this. The most difficult thing for me to figure out as my Clojure program grows and grows in complexity, is how to structure it, so it's easier to come back to. Blog posts, videos, and books (I would pay) would be welcome!

seancorfield17:04:30

In terms of code-size scale, we're at 82,000 lines these days and we're sort of constantly evolving how we organize that much code.

seancorfield17:04:47

We have a monorepo with about 30 subprojects now. All managed with clj and a bunch of deps.edn files (we have a "base" subproject that has the overall deps.edn file, and that is where our CLJ_CONFIG var points).

k.i.o10:04:38

what am i missing here?

(match '("M" "125.4,69.7" "rest1" "rest2") 
       [(["M" x & rest] :seq)] x
       :else :no-match)
=> :no-match

delaguardo10:04:42

(match '("M" "125.4,69.7" "rest1" "rest2") 
         (["M" x & rest] :seq) x
         :else :no-match)

mail46210:04:54

since the first pattern is [...] you'll want the value to be, say, ['("M" "125.4,69.7" "rest1" "rest2")]

mail46210:04:40

or as @ says 😎

k.i.o10:04:07

oh thanks, that was simple enough

shraddha.paktolus12:04:10

Hi , my compjoure route is (GET "/foo/:a/:b/:c" [a b c] (some-controller/controller-function a b c)) it is not working when my url is like localhost:8080/foo/1-2-3.html#/foo/bar

mail46213:04:58

the # introduces a URI fragment, which isn't part of the path component of the URI. compojure is only destructuring the path component, which is /foo/1-2-3.html (no :b or :c available)

hobosarefriends15:04:02

for example one that should work would be: localhost:8080/foo/1-2-3/bar/baz

hobosarefriends15:04:11

if you’re trying to send a url through a url (ergo β€œ`1-2-3.html#`β€œ) you’re going to have a hard time.

shraddha.paktolus07:04:56

actually i m converting existing site into clojure ....and trying to use as it is url from existing one .... so in compojure i couldn't use # in between url

hobosarefriends13:04:01

ye, makes sense. Good luck.

david.folkner13:04:02

I'm struggling to get spec/fdef to fire anything useful.

(require '[clojure.spec.alpha :as s])
(s/def ::name string?)
(s/def ::score int?)
(defn generate-new-player
  [name]
  {::name name
   ::score 0})

(s/fdef generate-new-player
        :args (s/cat :name ::name))

(s/explain ::name 42)
; => 42 - failed: string? spec: :user/name
(generate-new-player 42)
; => {:name 42, :score 0}
Edited: because I suck at remembering alt-enter

mail46213:04:31

were you expecting some kind of exception/warning for (generate-new-player 42)? keep in mind specs don't apply at runtime

david.folkner13:04:03

Ahh...I was. I just found that in the testing section...is that only for testing then?

qythium13:04:16

you'll have to instrument the function to throw exceptions for specs

qythium13:04:55

clojure.spec.test.alpha/instrument

mail46213:04:57

https://clojure.org/about/spec#_using_specs lists all the ways specs can be useful

david.folkner13:04:49

Ahh! That's a great link.

david.folkner13:04:49

So I could use instrument for generate-new-player or add a if (s/valid? ::name name) (...do stuff...) (throw...)

mail46213:04:09

could abbreviate the if/throw to assert

mail46213:04:21

but that's the general idea with spec

david.folkner13:04:39

Quick follow up question. I was planning on spec-ing some large data structures my applications was going to pass around and checking it in and out of of the data functions. Is that a terrible idea?

mail46214:04:26

not at all, depends on the nature of the data and fns but I don't see why not

david.folkner14:04:00

Is the best way to instrument all those functions? Or add those asserts?

mail46214:04:28

also depends on your fns and program. add asserts if you have some kind of error handling strategy. instrument in unit testing either way

mail46214:04:51

or just instrument at the repl if you're not automating tests

david.folkner14:04:52

Just getting started with Clojure, so unit tests are being planned and coming. I'll take a look. I was just trying to make sure that the name had to match...so adding some error handling there seems to be useful.

david.folkner14:04:23

Thank you for the help. Its nice having ya'll around as I learn in a vacuum.

mail46214:04:49

I'd go with asserts then, which will force you to build up the error handling aspect of the program πŸ™‚

david.folkner14:04:28

I notice there is a spec.alpha/assert that something I should look at?

mail46214:04:58

it's useful since you can toggle the assert check

david.folkner14:04:24

That seems useful.

mail46214:04:12

the main reason specs don't apply at runtime by default is not to penalize perf for correct programs, so toggle-able asserts is one way to give more flexibility

mail46214:04:55

but if you're not worried about perf (yet), you can stick with ordinary assert or valid?/conform checks too

david.folkner14:04:37

I'll do the s/assert so I can toggle later in life. I had kinda planned on a user entering name...that's where the hard assert was coming from.

mail46214:04:45

so if that's a hard requirement then you might not want to be toggling it ever. I'd actually consider opting out of s/assert for those cases

mail46214:04:40

but experiment and see what works best for you, I don't know your program 😎

mail46214:04:51

nice thing is there are many options

scotto14:04:31

Hi there, dear Clojurians: Can you tell me how is next different than rest?

scotto14:04:36

Duh - I should have just typed the query into google! :-?

david.folkner14:04:38

I said to myself, "Duh...I know this" then ran (next [1 2 3]) and (rest [1 2 3]) and they were the same...time to google this for me too!

scotto14:04:08

Apparently, what I’m able to gather, doing a next on an empty sequence returns nil. Doing a rest on an empty sequence returns ( ).

scotto14:04:42

This would be important if you’re wanting to do a exit condition on a empty collection.

david.folkner14:04:53

Yeah...`rest` is one of the primary seq workhorses according to https://www.braveclojure.com/core-functions-in-depth/#first__rest__and_cons. So rest is more sequence based and lazier than next is my understanding.

k.i.o14:04:20

as i understand it, next will evaluate element of lazy sequence to check if it has any value while rest just returns next LazySeq which maybe empty

david.folkner18:04:34

That seems right to me.

david.folkner15:04:02

Style question. When destructuring complex data structures, my linter (`joker`) gives a warning for unused binding when I use the :as <complex data structure>, but to me (and examples I've seen) it seems good practice to include that to let folks know what data structure the function is looking for.

david.folkner15:04:29

Example

(defn index-from-name
  [{:keys [::players] :as game} name]
Where a game map is given but never used, I really only want the players from within the game map.

david.folkner15:04:51

I guess I never actually asked a question. Question: what is the preferred style? I don't see anything in the style guide.

qythium16:04:31

I found that joker ignores the warning if you precede the binding name with an underscore: eg. _game

qythium16:04:19

At least in Haskell / ML languages it's a common practice to denote unused bindings that way, not sure if that's the case here in Clojure

david.folkner16:04:49

Style guide does say to use _ for unused variables, so that seems like a good compromise.

roman.bataev20:04:41

alternatively, this check can be turned off by setting unused-as to false: https://github.com/candid82/joker#optional-rules

lockdown-17:04:09

Is passing function calls as arguments bad practice?

michael.e.loughlin18:04:11

it depends on how much you like reading Lisp πŸ˜„ Another option (as well as let) is threading macros https://clojure.org/guides/threading_macros

dpsutton17:04:47

can you put an example?

lockdown-17:04:20

(fn1 some-arg (fn2 args)) where fn2 returns some data

dpsutton17:04:23

if you were to let bind all of those it would get ugly. this is a "salt to taste" situation.

dpsutton17:04:30

sometimes makes sense sometimes not

dpsutton17:04:12

an easy example to see is in arithmetic. (+ (+ 2 3) (* 3 4) (/ 9 3)) would look silly if each of the intermediate steps was in a let binding

lockdown-17:04:15

good example, yeah I'll put more common sense to it πŸ˜‰

dpsutton17:04:30

its a good question. but the answer is to just make it legible

jpsoares10619:04:04

Would be a problem to create tests outside the test/ folder in a lein project? I guess is not a problem in clojurescript as I define what's the entry point and there's the closure compiler for not used functions, but I don't know if similar things happen in the JVM.

noisesmith19:04:56

the test/ folder is just a classpath entry that is automatically used by leiningen (and other tools) when you ask it to run tests, any other directory can be used if you properly make your tool use it when running tests

noisesmith19:04:53

there's a leiningen config for folders to include when running tests

;;; Filesystem Paths
  ;; If you'd rather use a different directory structure, you can set these.
  ;; Paths that contain "inputs" are string vectors, "outputs" are strings.
  :source-paths ["src" "src/main/clojure"]
  :java-source-paths ["src/main/java"]  ; Java source is stored separately.
  :test-paths ["test" "src/test/clojure"]
  :resource-paths ["src/main/resource"] ; Non-code files included in classpath/jar.

noisesmith19:04:07

(from lein help sample)

lockdown-19:04:29

Any number to currency formatters out there for clojure?

alexmiller19:04:46

Java has a whole internationalized system for this in the jdk

alexmiller19:04:05

So look up how to do it in Java and use interop

jpsoares10619:04:47

:tests-paths seems to work

bill.h.tucker23:04:56

I'm having trouble getting connected to ms sql server:

Caused by: java.lang.Exception: Unsupported URI:  please, submit an issue request and we'll try to add it. Pull requests also welcome

bill.h.tucker23:04:35

the docs here: https://en.wikibooks.org/wiki/Clojure_Programming/Examples/JDBC_Examples#Microsoft_SQL_Server make it look correct. i'm just wondering if this URI really isn't supported.

seancorfield23:04:29

@bill.h.tucker I suspect that wikibooks page is horribly out of date...

seancorfield23:04:34

Oh gosh, yes, it's completely different to how you use that library these days 😞

seancorfield23:04:49

What version of clojure.java.jdbc are you trying to use?

seancorfield23:04:44

More up to date documentation is here http://clojure-doc.org/articles/ecosystem/java_jdbc/home.html (and that is linked to from the GitHub repo for the project https://github.com/clojure/java.jdbc/ )

seancorfield23:04:59

You're better off using the :dbtype hash map form of db-spec -- it's the preferred way and it's much better supported that raw URI strings.

seancorfield23:04:45

for your case it would be

{:dbtype "sqlserver" :dbname "DDDDD" :host "HOSTNAME" :user "UUUU" :password "XXXXX"}

seancorfield23:04:59

Assuming you have a modern version of clojure.java.jdbc (0.7.9 is current) and you also have the Microsoft SQL Server JDBC driver in your dependencies.

seancorfield23:04:12

In project.clj, that would be [com.microsoft.sqlserver/mssql-jdbc "6.2.2.jre8"]

seancorfield23:04:23

Also, feel free to ask any detailed questions about clojure.java.jdbc or database stuff in #sql (it's lower traffic and I'm more likely to see it there) @bill.h.tucker

seancorfield23:04:22

Ah, at least it looks like someone updated that wikibooks page to warn people the docs are out of date For the latest, most up-to-date community-managed documentation for the clojure.java.jdbc library, consult Using java.jdbc on Clojure Documentation. This WikiBooks page is written around a very old version of the library and most of the examples here will not work with newer versions. πŸ™‚

bill.h.tucker23:04:30

thanks for your help. I've been struggling with this for weeks.

seancorfield23:04:02

Sorry to hear you've been struggling, especially for weeks 😞

seancorfield23:04:41

If it's not obvious, I'm the maintainer of clojure.java.jdbc (and the upcoming alternative for it, next.jdbc), and I test it against ten databases, including MS SQL Server although my heaviest use of it is for MySQL (Percona 5.7) at work.

bill.h.tucker23:04:35

thanks for your help.