Fork me on GitHub
#beginners
<
2018-01-08
>
ajwerner05:01:06

is there a way to create a new sorted-set from a sorted set removing one element?

andy.fingerhut05:01:38

(disj my-sorted-set element-to-remove) returns the value you describe

ajwerner05:01:47

sweet! thank you

andy.fingerhut05:01:05

as it does for any set, regardless of whether sorted or no.

ajwerner05:01:15

remove was clearly not the answer, just didn't know how to find disj

andy.fingerhut05:01:00

Have you seen the Clojure cheat sheet? It tries to group functions into categories: https://clojure.org/api/cheatsheet

andy.fingerhut05:01:57

Clicking the "Source repo" link near the top of that page leads to a page that has other variants of it

ajwerner05:01:00

that's super handy

andy.fingerhut05:01:42

The Collections / Sets section has a 'Change' group of functions that contains conj for adding an element, and disj for removing.

andy.fingerhut05:01:05

Hovering the mouse over a function name should show the doc string, without having to click on it.

udit07:01:17

I was trying to use the with-redefs macro to change config while running the tests. However the new configuration was not getting picked up by the functions that were called. Here's a gist of what I was trying to do

(ns original.namespace)
(def config {:a :b})

(defn my-fn []
  (do-something config))

(ns different.namespace)
(deftest my-test
  (with-redefs [config {:c :d}]
    (my-fn)))

Caio Guedes09:01:55

@ghadi @noisesmith, apologize by the delay.

Caio Guedes09:01:28

@noisesmith I'm using the driver from the maven and the jdbc from cljars.

Caio Guedes09:01:53

the message error is org.postgresql.util.PSQLException: Can't infer the SQL type to use for an instance of clojure.lang.PersistentVector. Use setObject() with an explicit Types value to specify the type to use.

Caio Guedes09:01:54

this is the model, I'm trying to execute create-item function

andy.fingerhut10:01:52

@udit I'm not experienced using with-redefs, but when I try something like the code you show in a REPL, it says it is unable to resolve the Var config in the second namespace. If you do "(with-redefs [original.namespace/config {:c :d}] ...)" in your code, it may work as you hope.

andreasklein10:01:09

I am trying to catch an exception which occurs in a predicate function I use in a filter. I boiled it down to this snippet:

andreasklein10:01:35

Why can’t I catch this exception

Prakash11:01:26

Could be because of lazy evaluation

andy.fingerhut11:01:25

If you wrap the (filter ...) in a doall, i.e. replace it with (doall (filter ...)), then the exception is caught.

andy.fingerhut11:01:52

so lazy evaluation does sound like the reason.

andy.fingerhut11:01:47

The (try (filter ...)) is returning an unevaluated lazy sequence, so no exception has been raised yet. If you type that into a REPL, then the REPL will force the lazy seq returned from the (try ...) expression so it can print the value, and then the exception is thrown.

andreasklein12:01:47

@andy.fingerhut thank you.

joshkh13:01:49

are there any native sparse collections (other than perhaps using a map with indices as keys)?

Prakash13:01:08

there is int-map in contrib

joshkh14:01:35

thanks, i'll have a look 🙂

lilactown19:01:17

can anyone tell me why I can’t find anything in the namespace of the library I just published?

noisesmith19:01:56

@lilactown how are you accessing your namespace?

noisesmith19:01:32

a common mistake is using in-ns or ns to switch to a namespace, without using something like require that actually loads it first

lilactown19:01:21

I’m trying to use it in another project now

lilactown19:01:50

I think I figured out some of it… I had defined the namespaces in the library as lambda-tools.core instead of lilactown/lambda-tools.core

lilactown19:01:10

still having issues though. should it be (ns lilactown.lambda-tools.core)?

noisesmith19:01:52

you need to require the file (or otherwise cause it to be loaded)

noisesmith19:01:00

just switching to the ns doesn’t make the code load

lilactown19:01:25

user> (require 'lilactown.lambda-tools.core)
nil
user> (lilactown.lambda-tools.core)
CompilerException java.lang.ClassNotFoundException: lilactown.lambda-tools.core, compiling:(*cider-repl demoaws*:45:7) 
user> (ns-publics 'lilactown.lambda-tools.core)
Exception No namespace: lilactown.lambda-tools.core found  clojure.core/the-ns (core.clj:4032)
user> (ns-publics lilactown.lambda-tools.core)
CompilerException java.lang.ClassNotFoundException: lilactown.lambda-tools.core, compiling:(*cider-repl demoaws*:51:7) 
user> 

lilactown19:01:34

^this is what I’m doing in my separate project

noisesmith19:01:04

so, (lilactown.lambda-tools.core) isn’t valid - namespaces are not things you can call

lilactown19:01:36

I was just messing around. notice how I can’t get ns-publics to work?

noisesmith19:01:44

(ns-publics 'lilactown.lambda-tools.core) - throwing an error is an issue

lilactown19:01:57

autocomplete in CIDER doesn’t show anything either

noisesmith19:01:12

did you do anything else before require?

noisesmith19:01:17

eg. calling ns or in-ns ?

noisesmith19:01:34

because that would make require be a no-op - it does nothing if you don’t specify :reload and you already created the ns

seancorfield19:01:45

The version on Clojars seems to have (ns lilactown/lambda-tools.core ...) in it (0.1.2) -- have you pushed your updated version (with a . instead of /)?

lilactown19:01:46

that’s what I’m working on right now to see if it fixes

lilactown19:01:58

I don’t understand all the rules around namespacing yet

seancorfield19:01:30

Here's my REPL session with that

(! 863)-> boot -d lilactown/lambda-tools repl
...
boot.user=> (require 'lilactown.lambda-tools.core)

             clojure.lang.ExceptionInfo: Call to clojure.core/ns did not conform to spec:
                                         In: [0] val: lilactown/lambda-tools.core fails spec: :clojure.core.specs.alpha/ns-form at: [:args :name] predicate: simple-symbol?
                                         
clojure.lang.Compiler$CompilerException: clojure.lang.ExceptionInfo: Call to clojure.core/ns did not conform to spec:
                                         In: [0] val: lilactown/lambda-tools.core fails spec: :clojure.core.specs.alpha/ns-form at: [:args :name] predicate: simple-symbol?
                                          #:clojure.spec.alpha{:problems [{:path [:args :name], :pred clojure.core/simple-symbol?, :val lilactown/lambda-tools.core, :via [:clojure.core.specs.alpha/ns-form :clojure.core.specs.alpha/ns-form], :in [0]}], :spec #object[clojure.spec.alpha$regex_spec_impl$reify__2436 0x6e98787b "[email protected]"], :value (lilactown/lambda-tools.core (:require [clojure.data.json :as json] [clojure.string :as s] [ :as io])), :args (lilactown/lambda-tools.core (:require [clojure.data.json :as json] [clojure.string :as s] [ :as io]))}, compiling:(lilactown/lambda_tools/core.clj:1:1)
because you can't have / in a ns declaration.

seancorfield19:01:35

Artifacts -- library names -- are group/artifact but that does not need to match the actual namespace(s). Namespaces are foo.bar.quux -- no /.

lilactown19:01:10

I see. thank you!

seancorfield19:01:39

The namespace "path" should match your filesystem path under src with - in the namespace name replaced by _ in the filesystem path.

seancorfield19:01:01

So if you have src/lamba_tools/core.clj that is (ns lambda-tools.core ...)

lilactown19:01:46

is it then possible to have my library name be lilactown/lambda-tools, and in apps that use it require it like lambda-tools.core?

noisesmith19:01:28

yes the namespace and the artifact name don’t even have to be related (though it’s nice if they are somehow related)

lilactown19:01:10

awesome, thanks

seancorfield19:01:01

My bad. Pulling 0.1.3 fixed the problem

(! 865)-> boot -d lilactown/lambda-tools:0.1.3 repl
...
boot.user=> (require 'lambda-tools.core)

java.io.FileNotFoundException: Could not locate lambda_tools/core__init.class or lambda_tools/core.clj on classpath. Please check that namespaces with dashes use underscores in the Clojure file name.
boot.user=> (require 'lilactown.lambda-tools.core)
nil
And that matches your src path now (with lilactown in it).

lilactown19:01:19

yep. I’m iterating 😅

lilactown19:01:03

I decided I wanted to name the library lilactown/lambda-tools but have consumers use it in their code as lambda-tools.core or lambda-tools.middleware

seancorfield19:01:58

Although foo.core is the default in new project templates, it's an unfortunate anti-pattern. In your case, src/lilactown/lambda_tools.clj and (ns lilactown.lambda-tools ...) would probably be better naming

seancorfield19:01:40

(and src/lilactown/lambda_tools/middleware.clj and (ns lilactown.lambda-tools.middleware ...) to go with that)

lilactown19:01:53

🤔 I see. that is interesting

seancorfield19:01:03

Namespaces should be globally unique ideally, so someone could have both your lambda-tools and some other person's lambda-tools usable in the same app.

lilactown19:01:11

yeah, that makes sense. that’s what my original goal was, but it was annoying to have to fully qualify the namespace on each import

lilactown19:01:21

for some reason dropping .core makes me feel better about it 😛

noisesmith19:01:00

yeah - I agree that .core is and anti-pattern, it adds verbosity without adding to uniqueness in any way

seancorfield19:01:17

I suspect if Phil (`technomancy`) was creating Leiningen again today, he wouldn't use .core as a default namespace and he'd enforce the "no single segment namespace" guideline in templates 🙂

noisesmith19:01:11

if you run $ lein new lilactown/lambda-tools it already does the right thing today btw

lilactown19:01:40

I see. I ran lein new lambda-tools like a newb

lilactown20:01:17

i’m also ripping this code out of an existing project so that made it harder as well

seancorfield20:01:59

@noisesmith Oh, that's good to know -- no .core ns in that case?

seancorfield20:01:16

Hah, and boot/new does the same because it reuses a bunch of Leiningen stuff:

(! 867)-> boot -d boot/new new -n seancorfield/test-lib
Generating a project called seancorfield/test-lib based on the 'default' template.
The default template is intended for library projects, not applications.

Mon Jan 08 12:08:45
(sean)-(jobs:0)-(~/clojure)
(! 869)-> tree test-lib
test-lib
|____.gitignore
|____.hgignore
|____build.boot
|____CHANGELOG.md
|____doc
| |____intro.md
|____LICENSE
|____README.md
|____resources
| |____.keep
|____src
| |____seancorfield
| | |____test_lib.clj
|____test
| |____seancorfield
| | |____test_lib_test.clj
I wrote boot/new and I didn't realize that! 🙂

seancorfield20:01:48

(! 870)-> cat test-lib/build.boot 
(def project 'seancorfield/test-lib)
(def version "0.1.0-SNAPSHOT")

Jason Wheeler20:01:06

i am wondering if someone can explain this syntax to me, i'm reading the article at https://www.braveclojure.com/appendix-b/ and it has this code example

Jason Wheeler20:01:12

(defn whiney-str
  [rejects]
  {:pre [(set? rejects)]}
  (fn [x]
    (if (rejects x)
      (str "I don't like " x)
      (str x))))

(def whiney-strinc (comp (whiney-str #{2}) inc))
(whiney-strinc 1)
; => "I don't like 2"

Jason Wheeler20:01:02

whiney-strinc is created with def, not defn, but still called like a function, how does that work?

Jason Wheeler20:01:30

and also, what is this syntax: (whiney-str #{2})

dadair20:01:00

comp returns a function, so you are capturing that returned function into a var with def

Jason Wheeler20:01:57

oh ok, of course, that makes sense

dadair20:01:25

#{2} is the set containing the value 2, so (whiney-str #{2}) is calling the function whiney-str with the argument #{2}. whiney-str itself returns a function (fn ..), so (whiney-str #{2}) is "baking" the #{2} into the returned function

Jason Wheeler20:01:50

ahh ok, that makes sense as well, thank you

pablore21:01:33

How do I pair 2 collections with a Many-to-many-association-map?

pablore21:01:42

Is there like a builtin function?

noisesmith21:01:41

@pablore there’s some interesting functions in clojure.set - for example clojure.set/index and clojure.set/project

noisesmith21:01:53

not sure if any of them do exactly what you want, but might be a start

noisesmith21:01:12

there’s also group-by for simple indexing by some mapping function

andy.fingerhut23:01:24

@pablore Have you looked at zipmap? My main confusion about your question is what you mean by a many-to-many association map.

pablore23:01:19

Some code is better than a thousand words:

(def fruits [{:fruit "apple"}
                   {:fruit "pear"}])
(def names [{:name "alice"}
                     {:name "bob"}])
(def assocs [{:fruit "apple" :name "alice"}
                     {:fruit "apple" :name "bob"}
                     {:fruit "pear" :name "bob"}])

pablore23:01:57

(??? names fruits assocs) => {"bob" [{:fruit "apple"} {:fruit "pear"}]
"alice" [{:fruit "apple"}]}

phronmophobic23:01:30

(group-by :name assocs) is pretty close

phronmophobic23:01:58

not sure how to make ??? give exactly the same output without knowing more what constraints fruits, names, and assocs will have

phronmophobic23:01:34

ie. does fruits only have one key in every map

pablore23:01:57

in the fruits and names maps, there could be more properties like color, last name, etc. The assoc maps only know name and fruit

andy.fingerhut05:01:44

Sounds like what you want may be similar to a join operation in SQL? clojure.set/join may be able to do what you want.

andy.fingerhut05:01:07

Maybe what you want would be join-3-way, defined something like this:

andy.fingerhut05:01:13

(defn join-3-way [x y z]

andy.fingerhut05:01:02

(clojure.set/join (clojure.set/join (set x) (set z)) (set y)))

pablore13:01:52

This worked! Thanks

noisesmith17:01:36

@andy.fingerhut aha I knew it was one of those things in clojure.set 😄 I should study that namespace more

andy.fingerhut17:01:12

Yeah, join is probably one of the fancier functions available in that namespace. I've never used it for anything, but have read its code to learn what it does.

andy.fingerhut17:01:07

Basically a half a dozen functions in there implement some relational algebra operators on in-memory relations (relation = set of maps, like the relational theory that SQL is based upon).

andy.fingerhut17:01:18

union difference intersection are common to any sets, and can also be used in operations on relations. project and select are pretty straightforward in what they do. index seems quite specific to relations, and rename seems to be there almost purely as part of the implementation of join.

pablore17:01:42

Gotta go back to my Databases theory 101 notes