Fork me on GitHub
#beginners
<
2020-02-17
>
didibus01:02:28

Sometimes I feel a bit bad that I'm using -> with too many nested ->> like situations where almost everything is ->> except for one ->. But heck, I still do it

hindol03:02:19

Mixing threading macros makes code hard to read IMO. I try to follow Stuart Sierra's advice to the letter.

tjb03:02:44

YAHOOO!!! i was able to inject a database component into my defroutes im so stoked!

tjb03:02:29

fellow beginners: if you are interested in using the Component lib as I have been the past couple of days i highly recommend this video as well as the skeleton i linked before

seancorfield04:02:42

This example uses Component and has the Application Component injected into the request: https://github.com/seancorfield/usermanager-example

Michaël Salihi09:02:00

@seancorfield I just tried your user-manager repo, it's great to approch Component, thx. A beginner question : How can I handle the code change like lein-ring > auto-reload? ?

seancorfield17:02:50

@UFBL6R4P3 I never used lein ring because I don't like the "magic" of plugins: I prefer to start/stop the server directly in code (as that example shows) so it is easy to start/stop inside a REPL. I also avoid all "watch"-based workflows and auto-reload/auto-refresh. That stuff can easily get people into a mess because when it breaks (and it often does for people), most folks have no idea how to fix it and end up restarting their REPL anyway.

👍 1
seancorfield17:02:00

I'm with Stu Halloway and Eric Normand on avoiding all that stuff and working instead with a very simple REPL-based workflow and a very tight edit-eval cycle (often without even saving files: I eval every single change I make, as I make it).

Michaël Salihi18:02:10

@seancorfield One more time, thank you for your workflow details. In mean time, I had also find some information from you on Clojureverse 😉 https://clojureverse.org/t/ring-with-clojure-tooling/4732/3?u=prestancedesign

seancorfield04:02:07

So each controller can get at the application, the database, whatever.

seancorfield04:02:27

@tjb had you seen my usermanager repo before?

tjb04:02:20

yeah i shared it a couple days ago and tagged you 🙂

tjb04:02:29

ive been using that + the video to understand everything

tjb04:02:43

it has been key in me understanding everything (+ the help from Lennart)

tjb04:02:11

im very hyped about my progress haha

seancorfield04:02:52

OK, cool. I didn't scroll back far enough to see if it was a source 🙂

seancorfield04:02:46

Glad I could help!

seancorfield04:02:38

(our scroll back is pretty short!)

naoki86star04:02:26

Hi I'm enjoying clojure more and more in learning. I ask about a contat/conj result. In easy first step, I wanted to get a string array for command line like.

user=> (concat (conj ["/usr/bin/ssh"] host) cmdline)
("/usr/bin/ssh" "" "exit" "0")
I needed to add arguments from array. Then it was unexpected output.
user=> (concat (conj (concat ["/usr/bin/ssh"] args) host) cmdline)
("" "/usr/bin/ssh" "-p" "22" "exit" "0")
after all I got my wants
user=> (concat (concat (concat ["/usr/bin/ssh"] args) [host]) cmdline)
("/usr/bin/ssh" "-p" "22" "" "exit" "0")
I wonder what is important when using concat/conj. thanks

tjb04:02:46

@seancorfield do you mind if i ask you a question regarding jdbc.next here?

seancorfield04:02:17

@naoki86star The key thing is that conj adds items in the "appropriate" way for the type of collection. conj [] will add at the end. conj () will add at the beginning. concat produces a list, not a vector.

naoki86star05:02:37

Thanks for your advice.:man-bowing: First I started to use conj just on two word. I agree your example using concat simply when gathering some parameters.🙂

tjb04:02:46

so i am attempting to query my postgres db and i am getting the following erro

tjb04:02:52

No suitable driver found for jdbc:

tjb04:02:21

my config looks like this

(def ^:private pg-db
  "Connect map we will use to connect to PostgreSQL"
  {:dbtype "postgresql"
   :dbname "patentfitness"
   :host "localhost:3306/patentfitness"
   :user "USER_HERE"
   :password "PASSWORD_HERE"})

tjb04:02:39

me entry point is here

tjb04:02:41

(defn get-patent
  "Get a patent"
  [req]
  (let [db (-> req :database)]
    (println db)
    (sql/query db ["select * from patent"])))

seancorfield04:02:09

For your final example @naoki86star you could say

user= (concat ["/usr/bin/ssh"] args [host] cmdline)

valtteri04:02:20

You need to add postgresql driver explicitly into your dependencies

org.postgresql/postgresql   {:mvn/version "42.2.9"}

👍 1
seancorfield04:02:46

@tjb yup, like @valtteri says: you need the PostgreSQL driver as a dependency.

seancorfield04:02:03

next.jdbc doesn't add any database drivers -- you have to do that.

seancorfield04:02:08

The docs are very clear about that.

✔️ 1
valtteri04:02:12

Also there seems to be something funny with your jdbc-url. I see port there twice?

tjb04:02:16

hmmmm did i miss this in the next.jdbc docs?

tjb04:02:19

probably!

tjb04:02:34

def missed it then let me re-read

seancorfield04:02:49

Also, your :host is wrong. It should just be the hostname. You have the port and the database name in it too.

seancorfield04:02:14

PostgreSQL uses port 5432 by default. MySQL uses port 3306.

seancorfield04:02:47

I think you want

(def ^:private pg-db
  "Connect map we will use to connect to PostgreSQL"
  {:dbtype "postgresql"
   :dbname "patentfitness"
   :host "localhost"
   :user "USER_HERE"
   :password "PASSWORD_HERE"})

👍 1
tjb04:02:40

woooooot!

tjb04:02:52

everything works, thanks @seancorfield and @valtteri!!

tjb04:02:35

wow im ecstatic about this progress! hell yes!

seancorfield04:02:56

If there's anything that can be clearer in the next.jdbc docs, feel free to open issues on GitHub about it.

👍 1
tjb04:02:08

will do!

tjb04:02:17

thanks again everyone this slack channel is a lifesaver

tjb04:02:29

have a good night or good morning depending on your timezone 🙂

👌 1
naoki86star08:02:32

Hi , now studying on repl. When I changed to other namespace on repl, I annoyed for a while.

app.core=> (use app.sample)
Syntax error (ClassNotFoundException) .....
I've found to put a apostrophe before name-space string. I wonder what is this apostrophe? (ns app.sample)is okay...

andy.fingerhut10:02:38

The apostrophe is also called a quote, and 'expression can also be written (quote expression) . Such an expression is prevented from being evaluated.

andy.fingerhut10:02:26

use is a function, and like all functions in Clojure, all arguments are evaluated before the function is called.

andy.fingerhut10:02:27

ns is a macro, which you can't tell just from looking at the name, but using (doc ns) will tell you if it is a macro. Macros can have their own custom rules for whether they evaluate their arguments, or not. ns does not, and this is the difference between needing a quote for use , but not for ns

naoki86star10:02:19

I see. 💡💡 Thanks for unveiling my details that I didn't understand! These help me not only to let me know more but also will help to solve similar my trouble.🙂

teodorlu09:02:30

Hey! Quick question about what's what I should write in my ns declaration when using Java interop. The following appears to work:

(ns test.java-interop)

(defn ->pgobject [v]
  (doto (org.postgresql.util.PGobject.)
    (.setType "json")
    (.setValue (json/write-str v))))

(->pgobject {:x 1 :y 2})
;; => #object[org.postgresql.util.PGobject 0x133a7eee "{\"x\":1,\"y\":2}"]
is this what I should be doing, or should I add some :import things in the ns declaration?

leonoel09:02:10

You can do it like that, because java classes are automatically imported. The purpose of :import is to avoid typing package name multiple times.

👍 1
teodorlu09:02:33

Perfect. Thanks!

abdullahibra12:02:42

how can you write tests which pass values between each other?

mloughlin12:02:07

do you have a pseudo code example of your situation? It sounds like something I would normally avoid doing - I always learned that tests should be strictly independent of one another.

abdullahibra12:02:51

rest service for example: 1- create a record 2- update a record 3- get record 4- delete it

abdullahibra12:02:10

you need to get the id from step 1.

abdullahibra12:02:17

to pass to 2, 3 and 4

ackerleytng12:02:44

I try and do this by first testing the creation

ackerleytng12:02:52

Then assuming the creation works

ackerleytng12:02:04

Use the creation to test updating

ackerleytng12:02:44

Not sure if there's a better way to do it though

mloughlin12:02:03

IMO you have 5 tests there, one for each of your 4 steps and an end-to-end test, each can be run in their own separate database transaction (setup/test/rollback) EDIT: If you don't own the REST service can each step just go into one big test?

mloughlin12:02:37

What happens if you cause a bug in test 2? Tests 3 and 4 will be flagged as failing as well because they depend on 2. If you're working on the Delete functionality do you now have to run Test 1, 2, 3 every time you change Delete?

teodorlu13:02:48

On the other hand, in order to test deletion, there actually needs to be some value in the db, and if that is not working, you won't be able to test deletion. But I second not trying to make tests depend on each other.

mloughlin13:02:27

It comes down to what you're testing with each test, and why, which is annoying because it means you have to think really hard 🤯

teodorlu13:02:17

Mm. I was thinking one might be testing "too low level" if one intends to test a db write, instead of a piece of functionality.

mloughlin13:02:19

Fair enough. If it's a REST API you control there's probably DB crud tests already written!

Levi Costa14:02:40

I need help to where to start with clojure

Aviv Kotek15:02:08

I'd recommend to start with this MOOC http://iloveponies.github.io/120-hour-epic-sax-marathon/index.html Do all the exercises and you'd be fine.

Levi Costa14:02:23

i don't speak a very beautiful english 😄

Tzafrir Ben Ami14:02:01

https://www.braveclojure.com/foreword/ is a good online book to start with http://www.4clojure.com/ practice some of the fundamentals

❤️ 1
grounded_sage14:02:06

I’m a little confused with threads. From what I read you can have 2 threads per core. I’m using core.async async/thread . It seems I can run more than the math of 2x cores would allow. I would expect some kind of error here. What is it doing under the hood?

Michael J Dorian14:02:13

your hardware has a maximum number of threads it can run in parallel, simultaneously, but there is no limit to how many threads your hardware can run by juggling threads and switching between them quickly. In practice many threads are used for lightweight tasks that don't take much cpu time, such as waiting for input, so threads need to be able to be juggled to be useful

👍 1
teodorlu15:02:31

Three different concpts: 1. Your CPU has for example for hardware threads 2. Your OS runs hundreds of threads (one or more per app you're running) 3. With core.async you can use "green threads" that don't even use an OS thread (less overhead). In practice, you might be able to run hundreds of thousands of these.

Levi Costa14:02:48

@doby162 i looking for recommendation with getting started, books, etc,

Michael J Dorian14:02:38

Other than @tzafrirben's good suggestions, I would also suggest https://clojure.org/guides/getting_started But probably brave clojure first.

Levi Costa14:02:13

I'm migrating fom PHP, i belive i have difficulty

Michaël Salihi15:02:54

@UU380858C I come from PHP too. I begin to learning Clojure/ClojureScript since 8/9 month and the trip is awesome! So welcome! 🙂 Some links : • https://exercism.io/tracks/clojure (you can compare the results exercices with different language that you know like PHP for learning basics ) • If you're interested with front-end and SPA development, there are some videos for free : https://www.learnreagent.com/

❤️ 1
Aviv Kotek15:02:01

Any cleaner way to get-specific-map from vector-of-maps depend on specific value atm I do: (first (filter #(= (:key %) val) [m1, m2,....]) but this is not readable IMO and looks bad in the project

Tzafrir Ben Ami15:02:45

using a thread macro?

(->> [{:key 1} {:key 2} {:key 3}]
     (filter #(= (:key %) 2))
     first)
you can also replace #(= (:key %) 2)) with a “real” function

Aviv Kotek15:02:10

it'll look better but still looking for some other way not to use the filter/predicate/first work (too much)

Aviv Kotek15:02:19

hmm i'll stay with this for now I guess

jumpnbrownweasel15:02:02

@USPQF75AS, the (first (filter ...)) pattern is very common in Clojure for what you're doing. I recommend using it, especially if you are a beginner, since you will see it often in other's code.

jumpnbrownweasel15:02:08

However, you may also be interested in the some function, which returns the first truthy result of a fn you pass.

(let [ms [{:key 1} {:key 2}]
      val 1]
  (some #(when (= (:key %) val) %) ms))
=> {:key 1}

🚀 2
Aviv Kotek15:02:25

btw, is there any way to unpack from a sequence? in python I'd do something like res1, rest2 = foo()

Aviv Kotek15:02:44

as in this case I'd want to pick more than 1 item (right now we are picking first

Tzafrir Ben Ami16:02:32

You can destruct a sequence if this is what you mean (I'm not very familiar with python) (let [[a b c] [1 2 3]]...

Ben Sless18:02:57

Another sweet pattern I learned about recently, which is also more extensible, is

#(= (:k %) v) => (comp #{v} :k)
It's especially strong if you want to add other options the value can be, just add members to the set. cons: always. In this case, creating a set. If v is constant, however, you can define the entire composed function outside. Also, set lookup is slightly slower. You gain readability and flexibility, pay slightly with performance

Sam Heaton15:02:26

Can someone give me an opinion on this style of writing functions using let down to the return? I've found myself doing this and it helps me organize the code conceptually as well, but I probably could have used some kind of macro to chain things as well I asume. Example:

(defn map-with-changed-element
  "Returns a map with element identified by id modified by query-map"
  [id maps query-map]
  (let [args (walk/keywordize-keys query-map)]
    (let [element (list-filtered-by-id id maps)]
      (let [allowed-keys (select-keys args (keys element))]
        (let [new-element (merge element allowed-keys)]
          (assoc-in maps [(keyword id)] new-element))))))

jumpnbrownweasel15:02:46

Threading macros can be nice, but it is also very useful to have names for the intermediate values, just like you have done. But the let form doesn't need to be nested, and using a single let will make it more readable. Each binding in a let can refer to the previous bindings.

(defn map-with-changed-element
  [id maps query-map]
  (let [args         (walk/keywordize-keys query-map)
        element      (list-filtered-by-id id maps)
        allowed-keys (select-keys args (keys element))
        new-element  (merge element allowed-keys)]
  (assoc-in maps [(keyword id)] new-element)))

Sam Heaton15:02:08

Oh, that's cool. I did not realize I didn't have to nest them. Thank you!

jumpnbrownweasel16:02:05

Sure! It is a nice feature.

Ben Sless18:02:46

@UTHL3A325 wait until you learn about multiple bindings in for / doseq ! it's really useful! (TLDR, you won't need to write nested loops)

🙂 1
Michael J Dorian15:02:38

At first glance this looks like a good opportunity for the threading macro https://clojure.org/guides/threading_macros

👍 1
Aviv Kotek15:02:08

Are there any benefits of using defrecord instead of a simple map Let's say i'd like to implement a singly-linked-list in clj. I could either choose to define (defrecord Node [v next]) or do the same with a map {:v v1 :next n1} Ofcourse it's more readable but with the clojure notion of representing entities as maps, how is defrecord any useful?

Ben Sless18:02:07

I don't know if what you're implementing specifically requires a singly linked list, but clojure already has one out of the box, the cons cell.

(cons head tail)
returns the equivalent of
{:v head :next tail}
With the added bonus of already implementing the ISeq interface, meaning first and rest work on it, returning the value and tail, respectively.

alexmiller15:02:02

there are a number of nuanced tradeoffs

alexmiller15:02:19

records have a strong type, which can be useful to hook into protocol dispatch

alexmiller15:02:56

they are implemented as a class with fixed known fields - generally that can be more efficient in memory and field access than generic maps

alexmiller16:02:10

but that also means modifications yield a new record instance, not a map with structural sharing - the impacts of that vary, it really depends whether that's better or worse

Aviv Kotek16:02:21

So as a rule of thumb, what should I go for

alexmiller16:02:44

there is no one answer - it really depends on understanding the tradeoffs

👍 1
gerred16:02:56

i usually progress (using data around performance or evidence of some other form) from maps to records or types

alexmiller16:02:00

for making a new data structure like a linked list, that's actually what deftype is designed for

alexmiller16:02:25

(although I'm not sure why you wouldn't just use lists for that)

Aviv Kotek16:02:49

just needed a quick use-case before I post a question here 🙂

Aviv Kotek16:02:12

i'm actually trying to implement a zipper data structure

Aviv Kotek16:02:21

so i'd go for the record

MatthewLisp17:02:20

I have a SEQ of MAPS I want to drop EVERYTHING after ITEM X from the seq. how would you do that?

MatthewLisp18:02:47

made it with reduce

MatthewLisp18:02:07

it doesn't because i don't want to check anything, i want to drop everything that exists after item X

timcreasy18:02:23

Alternatively, perhaps that is just “take”?

MatthewLisp18:02:58

take is problematic because i would have to specify a fixed number, and since i'm working with a result from a 3rd party service... i don't trust it

MatthewLisp18:02:37

reduce can do it when i check for the last value from the empty collection that it's using

hindol18:02:14

take-while not X, and then add back X.

hindol18:02:27

With reduce is also okay. I presume you are short circuiting with reduced?

👆 2
jumpnbrownweasel19:02:37

Here is what @hindol.adhya said (take-while and add back) using concat:

(let [coll [1 2 3 4]
      val  3]
  (concat (take-while #(not= val %) coll) [val]))
=> (1 2 3)

hindol19:02:21

I like to write (take-while (complement #{val}) coll). It is a matter of preference I guess. Just throwing it out there as a possibility.

jumpnbrownweasel19:02:57

Yet another way: (take-while (partial not= val) coll)

alexmiller19:02:52

there's a take-until jira ticket out there with a variety of impls

alexmiller19:02:52

for a bunch of reasons (some mentioned there), I'd say odds are low that this will ever make it into core, so take what you need :)

lordie20:02:45

Hello : ) , I'm wondering if there is a package to covert data into "type metadata"

andy.fingerhut20:02:30

Can you give an example of what you mean by type metadata? And what kind of data you would want to convert into that?

lordie20:02:54

I think that I've misused the term, but I want something like this {:a 0, :b "aa"} -> {:a Num, :b String}

seancorfield20:02:30

You can call type on any Clojure value to get its type and you can use types as values -- but you'll likely want to massage the various numeric types to a common base type.

lordie20:02:33

using type I get PersistentArrayMap, but I want something more specific, seems like calling type on each value would do what I want

lordie20:02:45

so I'm wondering if there's a library for that

alexmiller20:02:17

there are one or two libs to infer specs from examples

alexmiller20:02:23

which seems in the ballpark

seancorfield20:02:12

@UTU1FCN73 Yeah, I meant you'd need to walk the data structure and call type on the values of it.

didibus06:02:29

Don't need a library, its a one liner:

(reduce-kv (fn[m k v] (assoc m k (type v))) {} {:a 0 :b "aa"})

chico21:02:02

How would I implement a spec that depends on another variable other the the actual value? For example, I’d like to define a spec for:

(defn first-element [sequence default]
  (if (or (empty? sequence) (nil? sequence))
    default
    (first sequence)))

(s/fdef first-element
  :args (s/cat :sequence seq? :default any?)
  :ret any?
  :fn ...)
In a static typed language (with some support to generics) I’d do something like:
first-element<T>(sequence: Seq<T>, default: T): T

alexmiller21:02:55

you can check that in the fn spec here

alexmiller21:02:11

but spec doesn't have parameterized specs

chico21:02:42

humnn got it! So do you thing any? is a reasonable spec or is there a better way of representing default in specs?

seancorfield21:02:00

@fran.fhb Note that seq? probably isn't what you want there: it means "does this thing implement ISeq?" and lots of things that behave as sequences do not. Also, empty? will return true for nil so you don't need both checks there.

chico21:02:49

Indeed! I refactored it and it works perfectly, thanks!

seancorfield21:02:13

You'll have to "step back" from static types when thinking about Clojure to some extent. Sequences (and collections) are often heterogeneous -- and Clojure does value-based equality checks on collections so you'll find, for example, that (= [1 2 3] (map inc (range 3))) which may be surprising if you're thinking in terms of static types.

chico21:02:50

Makes sense, I’ll try to switch my mindset! Thanks for the concrete example!

seancorfield21:02:35

Also, first calls seq on its argument so it can return the first element of anything that is seqable? and something that implements ISeq is treated specially, as an optimization.

chico21:02:01

From the docs for first, I guess, I could assume coll? would be a better predicate, right?

seancorfield21:02:50

That checks whether something is an IPersistentCollection -- seqable? is more general.

seancorfield21:02:18

user=> (coll? "Hello")
false
user=> (first "Hello")
\H

seancorfield21:02:45

user=> (seqable? "Hello")
true
user=> (seqable? [1 2 3])
true

seancorfield21:02:41

Sequences are an abstraction in Clojure, rather than a concrete type. Lots of things can be treated as sequences, because seq works on a lot of things.

chico21:02:42

Thanks! now it’s really clear!

NoahTheDuke01:02:18

sequence? might be closer, right?

seancorfield04:02:43

@UEENNMX0T what is sequence? I'm talking about built-in predicates that can be used in Spec.

seancorfield21:02:02

user=> (seq? [1 2 3])
false
user=> (first [1 2 3])
1

alexmiller21:02:23

this is a good example actually of exactly the kind of constraint we've been working on wanting to have better tools for in spec 2 around function specs

alexmiller21:02:41

a big difference from types and parameterized types is that spec is much more about sets of values

alexmiller21:02:11

the thing you really want to say here is that the result is exactly one of the values from the input

alexmiller21:02:19

it's not just that the input is a "coll of ints" and the result is an "int". it's the result is one of that set of values in the input collection.

chico21:02:33

Ohh that makes so much more sense now, thanks! I’ll follow the development of specs closer! thanks!

Mattias21:02:58

Risking my head, I just have to ask anyone who knows more - reading the leaves and the wind, one conclusion would be that Clojure asymptotes towards something like what Haskell does... that is, a ghost in the machine that acts on function definitions (and stuff?) that tells you if you’ve messed up. JavaScript emerges as TypeScript. Python, bastion of no types, gets types. Clojure gets something. Is this all inevitable? Is this always the path?

Mattias21:02:39

(Honest, if more than regularly confused, question.)

seancorfield22:02:16

Not sure I follow... Spec isn't a type system -- it's more powerful in some ways and it's available at runtime for data validation and conformance.

seancorfield22:02:42

It's true that some languages are getting optional/gradual type systems either within the language or as a dialect -- but I don't think that's inevitable (or even desirable, in many cases). Clojure has a dynamic system for specifying predicates that hold for values at runtime which is rather different.

seancorfield22:02:23

core.typed is an example of a library that provides an optional, external way to "type check" Clojure code but it is a research project (and it is in the process of being fairly substantially reworked, as I understand it, based on lessons learned from earlier incarnations and research into how data types flow through code).

andy.fingerhut22:02:39

Another view is that both Python and Clojure do have types, but it is not the symbols that name parameters and local variables that you associate with types, but all values at run time do have types, and very restricted ways that one can be converted to others. Haskell, C, Java, and many other languages associate types with names, and endeavor to check at compile time that you only do operations on those names that are allowed on values/objects of those types.

andy.fingerhut22:02:49

Where Clojure and Haskell are more like each other than different, is their strong emphasis on manipulating immutable values using pure functions. Haskell is in many ways more strict than Clojure is about this -- you can very easily write code that mutates data in Clojure, without having to put a special type signature on it like you would have to in Haskell. Except for a few languages (e.g. Erlang, and I think maybe Elm and Elixir), most languages are what I would call 'mutable data by default', meaning that while they can have libraries implementing immutable data structures, those libraries are not the default used by most developers of those languages, so if you try to develop in those languages with mostly immutable data, you cannot use most of the libraries of the language.

didibus07:02:09

Also mutable variables

didibus07:02:18

That's a big one as well

didibus07:02:27

Clojure variables are immutable by default. At least locals, and while vars are not, the convention is so strong here to not re-def that I'd consider it being immutable by default.