Fork me on GitHub
#beginners
<
2018-05-24
>
danygiguere00:05:43

thanks @hiredman. As soon as I add the line :jvm-opts ["--add-modules" "java.xml.bind"] to project.clj, I can run lein run -m myapp.core but nothing happens 😞

xtreak2903:05:53

http-kit 2.3.0 was released with the fix for JDK 9. You can remove the jvm-opts and upgrade http-kit

danygiguere00:05:51

(defproject myapp "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url ""
  :license {:name "Eclipse Public License"
            :url ""}
  :jvm-opts ["--add-modules" "java.xml.bind"]
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [compojure "1.6.1"]
                 [http-kit "2.2.0"]])

hiredman00:05:52

are you sure nothing happens, it doesn't throw any errors?

hiredman00:05:13

what is in myapp.core? do you have -main function?

danygiguere00:05:32

I just see a blinking cursor

danygiguere00:05:59

in myapp.core I have

(ns myapp.core
  (:require [compojure.core :refer :all]
            [org.httpkit.server :refer [run-server]])) ; httpkit is a server

(defroutes myapp
  (GET "/" [] "Hello World"))

(defn -main []
  (run-server myapp {:port 5000}))

hiredman00:05:37

what does run-server do?

danygiguere00:05:43

shoot, I just realized it’s actually runing on http://localhost:5000/

danygiguere00:05:10

I was expecting to see somthing in the console !!!

danygiguere00:05:34

well thank you @hiredman !!!

Philip Hale03:05:51

Just wanted to recommend Russ Olsen's Getting Clojure for anyone looking to learn the language. Coming from Ruby I was excited to find Russ had written something so comprehensive for Clojure, and it's the most accessible of the books I have access to. I'm working through Joy of Clojure, referring to Getting Clojure when I get stuck or want more background....it's a nice combo!

jumar11:05:24

Yes, I can even recommend it as a refresher if you've been playing with Clojure for some time. There are some interesting bits that you don't find in other books - I wrote a short review here: https://www.goodreads.com/review/show/2336863949

Caio Guedes20:05:28

I’m current reading https://www.braveclojure.com/, do you think Getting Clojure is a good choice after I finish it? @U06BE1L6T

jumar06:05:26

I think it's a good choice in general but I guess I tend to read lot more than people normally do. If you've already started Brave Clojure and like it, it may be better to stick with it and experiment a lot - exercises, pet projects etc.after you gain some real experience you may want to read Clojure applied or other more advanced book

jumar06:05:26

It might be best to glance through it quickly (inspectional reading) and decide on your own. You can even use it as a supplementary material if something is not clear in Brave Clojure.

jumar06:05:23

Back then I started with Living Clojure which was nice too - it mentions some web related libraries, community resources, etc. too

jumar07:05:45

If you are interested you can check out the post I wrote back then: https://curiousprogrammer.net/2017/02/15/my-clojure-learning-strategy/

Caio Guedes11:05:38

Thanks @U06BE1L6T, I’ll check this out!

lodin13:05:03

What is the current consensus on using <project>.core as your main library file? Is that still the encouraged scheme?

manutter5113:05:38

I think that’s the most common convention.

alexmiller13:05:42

not encouraged or discouraged - do whatever you want

danm13:05:33

We're making a lot of use of functional core/imperative shell, so a lot of our main lib files are <library>.shell 😉 Or we end up in a situation where you import <library>.core in your <project>.core (for composable builder functions etc) and <library>.shell in your <project>.shell (for the functions that actually execute the built requests)

quadron14:05:15

is there a general library that provides cursors for regular clojure atoms?

drewverlee14:05:04

i’m curious, whats does cursor mean in this context?

quadron14:05:45

I need to split the atom into smaller atoms (cursors).

quadron14:05:41

these smaller atoms are a view over some part of the bigger atom

quadron14:05:06

such that when the bigger atom changes, the change is reflected in the smaller ones

quadron14:05:16

mathematically there would be a poset structure between the atoms, with the minimum being (atom nil) and the maximum be (atom whatever-the-whole-state-is)

quadron14:05:13

poset-like*

quadron14:05:16

partial order

quadron14:05:34

like reagent atoms

quadron14:05:56

the whole poset would be a reactive ensemble in time

quadron14:05:17

do you know what i mean?

drewverlee14:05:06

not quite. Atoms can have invariants. You can used refs to coordinate change across multiple pieces of state, but usually ppl usually stick the simpler model by pushing all there state into an atom.

drewverlee14:05:39

You can use something like Datascript to make it easier to deal with the data.

drewverlee14:05:53

If the issue is querying semantics.

quadron14:05:21

the issue is names

drewverlee14:05:27

I believe Om Next (now fulcro) had cursors, i never fully grokked the idea. Maybe ask there?

quadron14:05:30

say I have a namespace that relies on just one piece of the state. I don't want that namespace know anything about the state other than the part that concerns it

sundarj14:05:00

it was the old version of om that had cursors

sundarj14:05:03

om next is completely diff

drewverlee15:05:42

@UA2JG2Y11 i understand what you just said about state, but i haven’t understood what you mean by “know about”. If you keep the state in a atom, and two things have an invariant about how they need to update together, then it seems to solve this problem of “isolation” which i feel is more about “correct coordination”. But i dont have a background in this type of thing, so you might have some concerns or ideas i dont understand 🙂

sundarj15:05:48

sounds like what component, integrant, and mount are about

quadron15:05:30

@U61HA86AG seems to me that those libs are smuggling oop into clojure, which is what I am trying to avoid

quadron15:05:56

let's say the whole state atom x is a database like datomic, and I want to shard that database such that different namespaces can have access to parts of it through names x1 x2 x3

quadron15:05:31

if that make sense!

quadron15:05:32

@U0DJ4T5U1 I mean, a function that only cares about the :foo key in the state, be passed an atom called foo that is in sync with the state

quadron15:05:17

(def state (atom {:foo "bar" :other "bullshit"}))

quadron15:05:52

(def foo (derive-atom state [:foo]))

drewverlee15:05:43

Can you define what you mean by “care about”?

drewverlee15:05:38

typically ppl have to solve the problems that sharding their DB (Separating it in space) causes.

drewverlee15:05:34

because now you have to manage state across time and come up with a coordination protocol that factors in time.

drewverlee15:05:44

I’m trying to understand what you stand to lose by putting the state in one atom and just having different query functions that access different parts. The results of which, could be feed to functions that are co-located in namespaces how ever you want. I’m genuinely interested in what your talking about, i have thought about this a bit myself.

quadron15:05:04

f cares about x (where x is a part of an atom called s) = f only reads and modifies the x part of the state

drewverlee15:05:30

I feel like were talking past each other. Here is a simple of example of what i mean: I store my state in a atom: (def foo (atom {x: 1, y: 2})) two functions: (defn update-x (swap! foo update [:x] inc)) (defn update-y (swap! foo update [:y] inc)) These functions can be put in your separate namespaces. They only know about x and y. This seems to fulfill your needs.

quadron16:05:30

is there a way to bind the value of :x to an atom called x and pass x instead?

drewverlee17:05:47

Would putting x in it's own atom do that?

rnagpal17:05:34

@UA2JG2Y11 reagent reaction might be useful

rnagpal17:05:03

This is how re-frame subscriptions work

rnagpal17:05:09

which listen on part of the db

rnagpal17:05:28

You can use make-reaction like (reagent.ratom/make-reaction #(get-in @parent-atom [:path :path]))

joaohgomes15:05:37

I don't know if I understood correctly, but what came in to my mind was using add-watch to track chances to the source atom. https://clojuredocs.org/clojure.core/add-watch

joaohgomes15:05:05

Unless it is a computational intensive task, and you want to prevent recalculation you could always derive from source atom

quadron14:05:22

like reagent cursors?

troglotit14:05:12

Do you have some rule-of-thumb regarding namespaces/-qualified keywords/functions? I don’t use ns-qualified keywords and while I’m designing my namespaces - I just put everything together waiting to something to emerge, and it seems fine. And I’m thinking to introducing namespace-qualified keywords to my project, and I have fears that I’m going to get a lot of recursive namespaces, so I’d have to namespace my keys fully :project.name.lib/keyword instead of (:require '[project.name.lib :as lib]) ::lib/keyword

troglotit15:05:31

So, to paraphrase, the question is “when does keyword belong to this namespace”

hiredman15:05:31

the namespaces used for keywords and the namespaces used to organize code are not the same

hiredman15:05:43

data will live for a long time, often in external stores (databases), code changes so often and so much that version control exists, so tying names in data to names in code isn't a great idea

hiredman15:05:46

aliased keywords ('') tie names in your data to names in code, and also make your code brittle (moving a function to another namespace suddenly changes the data it returns)

johnj16:05:45

namespace keywords vs real namespaces confused me at first too, the book I was reading didn't make a clear distinction I think

johnj16:05:19

@hiredman so in general you are against namespace keywords?

alexmiller16:05:08

I don’t think that is at all what he said :)

troglotit16:05:19

I’m having feeling, that it shouldn’t be a problem: everytime there’s a recursion, you just extract those keywords to separate namespace.

hiredman16:05:52

@lockdown- I am against using the same namespace names for code (often changing) and data (accumulating)

hiredman16:05:22

the distinction is maybe more of a thing in larger projects. I would prefer something like :projectname/user to :some.namespace.in.project/user, and maybe even :orgname/user

seancorfield17:05:13

@troglotit You can use (alias 'lib (create-ns 'project.name.lib)) without needing an actual code namespace for your ::lib/keyword

johnj17:05:59

@hiredman ah got it

seancorfield17:05:27

@lockdown- An adjunct to that is that I tend to have data specs in their own namespace with almost no code (perhaps just some predicates that those specs use) so, again, qualified keywords that don't match code namespaces (although they match the spec namespace).

Abstract Armen20:05:24

Hi guys, I have issues with using cljss package. Sorry, maybe it's so stupid, but I'm not able to import macros (:require [cljss.core :refer [defstyles]]). I always get error Invalid :refer, var cljss.core/defstyles does not exist. It's official usage recomendation from package github page https://github.com/roman01la/cljss. Any ideas?

seancorfield21:05:32

@UAWQ6PNGN In ClojureScript, I believe you have to use :require-macros -- see https://clojurescript.org/guides/ns-forms

SoV421:05:31

Hi. How would one go about printing out a result of all the :tokens in a tree or nested structure?

noisesmith21:05:27

one option

user=> (into []
             (comp (filter #(and (coll? %)
                                 (= (count %) 2)
                                 (= (first %) :token)))
                   (map second))
             (tree-seq coll? seq
                       {:token "a" :b {:token {:token "c"}}}))
["a" {:token "c"} "c"]

noisesmith21:05:11

I may have misunderstood your definition of "token" here

dfcarpenter21:05:57

So i'm struggling with designing my application in clojure. It's less a problem with clojure but moreso thinking functionally. I mostly write boring business apps and i'm used to OOP. Anyways, using records and functions to encapsulate a domain in my app has been a mental challenge to switch to and i'm wondering if anyone can point me to some non trivial yet still simpleish repos/examples of clean well designed clojure programs

SoV421:05:50

@dfcarpenter apps that i really admire, there are many of them out and about! I don't know what you're wanting to make, here is a really nice application for online chat that uses a server and a clientside db. incremental commits on the server are broadcast out to all peers -- it's really very efficient and fast, and i think the code is nice for reading. i'm always looking for good source to read so i'm curious what others will say http://tonsky.me/blog/datascript-chat/

dfcarpenter21:05:59

@sova Thanks, ill check it out.

dfcarpenter21:05:45

Also are there any best practices around when to use defrecord, defprotocol and deftype. It seems many people do without them but they can serve a purpose. Is seems its more than a preference and each confer some utility but knowing when seems tricky

SoV421:05:13

Hmm, they all sound good for clarity sake when appropriate. I don't know of any good use-cases for those, myself. However, there is more info on the selection here: https://clojure.org/reference/datatypes

noisesmith21:05:33

@dfcarpenter a good rule of thumb is to use hash-maps and functions whenever possible, when you need runtime extension a defmulti is better than a regular function, if you have multiple methods that go together, you can dispatch on type, and you need runtime extension, then protocols make sense.

noisesmith21:05:52

you'll usually want a record with a protocol, sometimes a reify or deftype

dfcarpenter21:05:22

@noisesmith Ahh ok that was helpful. Thanks

noisesmith21:05:48

it's very easy to turn code that's function on map into protocol / defrecord code

noisesmith21:05:01

(the opposite is less likely true)

noisesmith21:05:29

so use functions and maps until you need features these other things provide

justinlee21:05:45

@dfcarpenter I found that when I wrote oop code, I rarely actually used polymorphic features (and when I did, it would have been better to compose rather than inherit). I mostly used them for organization and naming. you can accomplish that by putting your “methods” in a namespace and passing around a map that would previously have been the “properties/fields” of your object. Suddenly you’ll find that many methods only need a tiny portion of the object state, and you can make them simpler by passing only what they need. My point here is that if you don’t need polymorphic dispatch, you don’t need that many language features.

noisesmith21:05:10

that's true, it's a good explanation of what I meant by "just use maps and functions"

SoV423:05:18

Hi, I want to "conj-in" at atom I have... I'd like to associate multiple numbers ... sets.. #{12 24 48 etc} with keywords in a part of my atom. how can i do append-only operations into the set

noisesmith23:05:17

if I understand you correctly, the pattern often looks like this:

(ins)user=> (def a (atom {}))
#'user/a
(ins)user=> (swap! a update-in [:a :b] (fnil conj #{}) :x :y :z)
{:a {:b #{:y :z :x}}}
(cmd)user=> (swap! a update-in [:a :b] (fnil conj #{}) :l :m :n)
{:a {:b #{:y :n :m :l :z :x}}}

SoV423:05:04

lovely, now say the keyword :b is actually determined by a list of strings... how can I run this swap line for each string in a list?

noisesmith23:05:13

you could do it inside a doseq on the list

noisesmith23:05:59

or make a single function that does all of the update-ins to reduce contention on the atom if that's a concern

SoV423:05:41

hmm. it's not a huge concern, but sounds like it would be faster

noisesmith23:05:53

#(-> % (update-in ....) (update-in ....) ...) - common pattern in reagent where each swap is a re-render of something

noisesmith23:05:41

I guess if it's a list of keys at runtime, you want reduce instead of ->

SoV423:05:28

I'm not sure how to use doseq on a vector of strings.

noisesmith23:05:53

same way you doseq on anything?

sundarj23:05:19

user=> (def strs (atom []))
#'user/strs
user=> (doseq [s ["hello" "world"]] (swap! strs conj s))
nil
user=> (deref strs)
["hello" "world"]

noisesmith23:05:45

same idea, yeah

(ins)user=> (doseq [s ["a" "b" "c"]] (swap! a update-in ["strings" s] (fnil conj #{}) "hello"))
nil
(ins)user=> @a
{:a {:b #{:y :n :m :l :z :x}}, "strings" {"a" #{"hello"}, "b" #{"hello"}, "c" #{"hello"}}}

SoV423:05:28

Oh hey, beautiful

SoV423:05:31

Thank you very much

sundarj23:05:18

can also do this @sova

user=> (run! (partial swap! strs conj) ["hello" "again"])
nil
user=> (deref strs)
["hello" "world" "hello" "again"]

SoV423:05:45

oh cool, i have been trying to wrap my mind around partials lately.

sundarj23:05:15

(partial f arg1 arg2) = (fn [& args] (apply f arg1 arg2 args)) essentially

athomasoriginal23:05:56

I have a function that looks like this:

(defn props 
  [val-1 val-2 val-n-1 val-n-2]
  {:prop-1 val-1
   :prop-2 val-2
   :prop-3 {:nested-prop-1 val-n-1
                  :nested-prop-2 val-n-2})
It was created because I was originally writing the map everywhere and it just felt like a lot of duplication. So I moved it into a function. Now I am running into a situation where I have the same function above, but I duplicated it everytime I needed to add another property (just coding quick) because I need the same basic map structure with the addition of one other property here and there. Two questions: 1. What would be an idiomatic way of reducing a lot of this duplication? One solution is something like (defn [] (assoc (props val-1 ...) :new-prop val-3 )) but Im not sure that is the best way 2. Given that the function has 4 params, might it be better written using keys?

noisesmith23:05:20

if you provided a map, it wouldn't be far from this idiom for expanding flattened paths

(cmd)user=> ((fn [m & kvs] (reduce (fn [m [ks v]] (assoc-in m ks v)) m kvs)) {:a 0} [[:b :c] 1] [[:d :e :f] 2])
{:a 0, :b {:c 1}, :d {:e {:f 2}}}

athomasoriginal23:05:02

Sorry, you mean if I update my props function to accept a map and vectors like (as you illustrated above) {:a 0} [[:b :c] 1] [[:d :e :f] 2]

athomasoriginal23:05:42

This is where functional programming blows my mind. I like this solution, but why does it feel like it requires a little more thinking for a consumer (another dev) to figure out what they are allowed to pass? What I am thinking is the consumer would have to know the exact structure that they need to pass into what has become, a very generalized function.

athomasoriginal23:05:24

For example, everywhere this is called, when passed to a particular function, the caller would have to use, for example, {:a 0} [[:b :c] 1]

noisesmith23:05:32

what I mean is that if it takes a map for the args, you can substitute keys to paths and use the above idiom

noisesmith23:05:16

(and that substitution can be performed with a simple hash-map)

athomasoriginal23:05:46

hmmm, I think I am missing it 😞

noisesmith23:05:05

never mind, it probably doesn't apply here

noisesmith23:05:31

it's a way to transform a flat sequence of paths and targets into a nested structure

athomasoriginal23:05:32

hmmm was the suggestion to update what I had to something like this:

(defn props 
   [{:keys [...]}]
   (fn [m & kvs] (reduce (fn [m [ks v]] (assoc-in m ks v)) m kvs)))
So I remove my hardcoded map and pass in a map as the arg like (props :val-1 0 :val-2 "hi")