beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
Michael Lan 2021-03-19T02:12:37.123400Z

Is there any difference between using partial and an anonymous function? Anonymous functions seem more readable and flexible to me at least.

2021-03-19T16:20:15.172Z

I like partial, comp, etc. because they are much less powerful, so they don't require the same level of close reading as fn / #()

seancorfield 2021-03-19T16:45:46.172400Z

I still like comp — I find I’m using it much more now we have transducers. I have to be honest and say that I don’t, generally, like partial. Over the years, we’ve moved away from it in our work codebase, either to explicit arities that provide just the intended amount of currying or anonymous function invocations, depending on context.

2021-03-19T02:33:31.124300Z

Anonymous functions are definitely more flexible.

2021-03-19T02:34:02.124500Z

I am not aware of any noticeable performance difference, or memory use difference, but haven't tried to measure them.

2021-03-19T02:34:25.124700Z

I prefer anonymous functions for their flexibility, personally, but some people like things such as comp, partial, etc.

2021-03-19T02:47:19.124900Z

A big difference with partial that very often trips people up is partial is just a function, like any other function and its arguments get evaluated that way

2021-03-19T02:49:33.125100Z

So for example (partial str "foo") derefs #'str once, and then closes over the value, where #(str "foo" %) derefs every time it is invoked

seancorfield 2021-03-19T03:15:28.125400Z

And it may or may not make a difference to you that Rich Hickey has said he considers anonymous functions more idiomatic than partial.

Michael Lan 2021-03-19T03:38:23.125600Z

Thank you for the detailed responses! Appreciate it a lot.

cschep 2021-03-19T04:52:44.126900Z

hey all, i have a map that maps an id to a map that represents a player, i have a list of maps that represent a collection of stats, those maps contain the player’s id, which is the key to the first map

cschep 2021-03-19T04:53:13.127600Z

i’d like to make each map in the list a member of the map that id points to

cschep 2021-03-19T04:53:16.127800Z

in the first map

cschep 2021-03-19T04:53:30.128200Z

in a more imperative language i’d write a loop on the second one and update the first one

cschep 2021-03-19T04:53:43.128400Z

any pointers for the clojure take?

cschep 2021-03-19T04:53:59.128800Z

is it appropriate to use map just to loop over each value?

cschep 2021-03-19T04:54:04.129Z

feels .. wrong

cschep 2021-03-19T04:54:19.129400Z

for also seems like it’s more for building a list

cschep 2021-03-19T04:55:12.129600Z

oo maybe doseq

dpsutton 2021-03-19T04:56:21.129800Z

(reduce (fn [player-info {:keys [id] :as stats}]
          (update player-info id update :stats (fnil conj []) (dissoc stats :id)))  ;; fnil conj because i don't know how many maps will be there so just keep a list of them
        {1 {:name "bob"}  ;; id -> map that represents a player
         2 {:name "sue"}}
        [{:id 1 :strength 8} -> sequence of maps with an id and other keys
         {:id 2 :strength 12}])
->
{1 {:name "bob", :stats [{:strength 8}]}
 2 {:name "sue", :stats [{:strength 12}]}}

cschep 2021-03-19T04:57:20.130Z

reduce eh

cschep 2021-03-19T04:57:22.130200Z

what’s fnil?

cschep 2021-03-19T04:57:23.130400Z

😄

dpsutton 2021-03-19T04:58:00.130700Z

check out (doc fnil)

cschep 2021-03-19T04:59:23.131600Z

oh that’s cool

cschep 2021-03-19T04:59:44.132300Z

i hadn’t thought about making a whole new thing, i was thinking about “updating” the original map

cschep 2021-03-19T04:59:46.132700Z

but that’s probably the wrong idea

dpsutton 2021-03-19T04:59:50.132800Z

but it's how to wrap a function and give it a substitute value for nils.`(+ nil 1) #_vs ((fnil + 0) nil 1)`. I used (fnil conj []) so that when it update's the :stats key on a player map, if there's nothing there it will "substitute" an empty vector and use that. its a common idiom

dpsutton 2021-03-19T05:00:27.133600Z

these are immutable data structures so i have made new things but they efficiently share structure with the old things

dpsutton 2021-03-19T05:00:38.133900Z

and in that sense you can't update the original

cschep 2021-03-19T05:00:44.134100Z

right, right

dpsutton 2021-03-19T05:01:19.134700Z

and to be a bit more precise its because they are immutable and persistent data structures. (if you find yourself looking them up at some point)

cschep 2021-03-19T05:02:07.134900Z

ok thanks!

cschep 2021-03-19T05:02:11.135100Z

a bit to chew on here

seancorfield 2021-03-19T05:04:23.137100Z

@cschep The biggest mental shift, coming to Clojure -- depending on your background, is effectively the removal of the assignment operator, and with that the whole idea of a traditional "loop" (because in most languages those are inherently based on side-effects and, of course, the loop condition inherently assumes that you're assigning new values to something).

cschep 2021-03-19T05:06:00.137500Z

yeah, totally

cschep 2021-03-19T05:06:05.137800Z

;create league out of players
(def league (atom (into {} (map (juxt :id identity)) sb-players)))

(defn get-player
  [id]
  (get @league id))

(defn update-player
  [id key value]
  (swap! league
         (fn [current-state]
           (assoc-in current-state [id key] value)))
  nil)

;import stats
(doseq [stats hitting-stats]
  (update-player (stats :id) :stats stats))

cschep 2021-03-19T05:06:19.138200Z

it does feel like i essentially wrenched the language into a mutable class

cschep 2021-03-19T05:06:20.138500Z

ha ha ha

cschep 2021-03-19T05:06:50.138700Z

but it does “work”

seancorfield 2021-03-19T05:08:30.140Z

For some context:

Clojure source 348 files 88298 total loc,
    3554 fns, 898 of which are private,
    571 vars, 29 macros, 91 atoms,
    849 specs, 33 function specs.
Clojure tests 378 files 23437 total loc,
    4 specs, 1 function specs.
That's our codebase at work. 91 atoms in just over 110K lines and most of those are caches of some sort.

robertfw 2021-03-20T02:02:20.187Z

@seancorfield what do you use to generate this summary?

seancorfield 2021-03-20T02:59:05.187200Z

@robertfrederickwarner Just a dumb shell script that knows the structure of our repo. It's just fancy wc -l and some fgrep calls.

cschep 2021-03-19T05:09:14.140700Z

yeah so the context of this is i’m using this atom instead of talking to a database

2021-03-19T05:09:23.141200Z

When you are using immutable data, you are always building new things from things you have (or things you are reading in from outside).

cschep 2021-03-19T05:09:37.141400Z

so there should just be the one

cschep 2021-03-19T05:09:44.141700Z

make json out of it and return it from web routes

cschep 2021-03-19T05:09:49.142Z

and update it by getting posts

cschep 2021-03-19T05:10:16.142400Z

is that a sane way to do this? I’m so burned out of writing database migrations and the scale of this is 1

seancorfield 2021-03-19T05:11:07.143Z

If you just have a single process, in memory, with no persistence, an atom makes a fine "database" 🙂

cschep 2021-03-19T05:14:09.144500Z

spitting it to a flat file

cschep 2021-03-19T05:14:15.144700Z

voila, persistence!

seancorfield 2021-03-19T05:15:15.145700Z

Yup. And the Agile crowd advocate for that quite strongly -- several of the original signatories to the Agile Manifesto are proud of how many projects they've built that don't use a database 🙂

cschep 2021-03-19T05:15:40.145900Z

ha ha, ok awesome

cschep 2021-03-19T05:16:27.146400Z

i suppose when i’m updating based on id’s and joining maps together based on keys it leaks out that i’m already in the database mindset

cschep 2021-03-19T05:16:31.146700Z

but it’s fun to poke around in your own little world

dpsutton 2021-03-19T05:18:03.147300Z

check out clojure.set/index to solve your above problem in a different way. you could keep them as separate collections and just "join" when you need to

😲 1
cschep 2021-03-19T05:20:17.147500Z

wow

cschep 2021-03-19T05:20:35.147800Z

how do you learn about this stuff and then go write javascript

cschep 2021-03-19T05:20:38.148100Z

for 8 hours a day

cschep 2021-03-19T05:20:40.148300Z

not gonna work

cschep 2021-03-19T05:20:44.148500Z

🙂

seancorfield 2021-03-19T05:25:18.148800Z

Easy: I never write JS 🙂

cschep 2021-03-19T05:25:30.149200Z

ha, bingo

seancorfield 2021-03-19T05:26:00.149800Z

I started learning Clojure in 2010 and started using it in production in 2011 -- and I pretty much never write any other code now.

2
cschep 2021-03-19T05:26:16.150100Z

what were you writing before?

seancorfield 2021-03-19T05:26:55.150800Z

A long arc: C, assembler, COBOL, C++, Java, ColdFusion, Groovy, Scala... then Clojure.

seancorfield 2021-03-19T05:27:19.151200Z

(there have been other languages in there, but those are the main ones)

cschep 2021-03-19T05:27:49.151400Z

sounds like you landed in a good spot

cschep 2021-03-19T05:27:50.151600Z

🙂

cschep 2021-03-19T05:31:50.152300Z

i’m gonna keep firing noob questions but i hope no one feels too obligated to keep up

seancorfield 2021-03-19T05:32:05.152900Z

That's what we're all here for in this channel!

cschep 2021-03-19T05:32:10.153Z

i’m trying to pass the function java.lang.String.contains to a function

cschep 2021-03-19T05:32:50.153900Z

.contains doesn’t seem to work

seancorfield 2021-03-19T05:32:50.154Z

You have to wrap Java methods in a Clojure function -- Java methods are not "first class functions".

cschep 2021-03-19T05:32:58.154200Z

ahh ok i’m glad i’m not crazy

cschep 2021-03-19T05:33:23.155100Z

#(.contains % %1) like that?

seancorfield 2021-03-19T05:33:38.155500Z

But look at the clojure.string namespace. It has an index-of function that returns nil for no match else the index of the match.

cschep 2021-03-19T05:33:46.155700Z

ohhh that will probably work

seancorfield 2021-03-19T05:35:41.157100Z

https://clojure.github.io/clojure/clojure.string-api.html has a lot of nice wrappers for the underlying Java (or JavaScript) functionality that lets you avoid interop syntax and reflection.

seancorfield 2021-03-19T05:37:04.157600Z

There's also re-find (in clojure.core) for doing regex pattern matching.

cschep 2021-03-19T06:09:38.158Z

oh yes, how to programmatically construct a regex?

cschep 2021-03-19T06:10:07.158600Z

finding a string that contains a string would be like #“.thestring.” or something?

cschep 2021-03-19T06:10:11.158800Z

can you string interpolate into them?

cschep 2021-03-19T06:10:48.159Z

oh.. re-pattern

cschep 2021-03-19T06:10:48.159200Z

i think

em 2021-03-19T06:29:12.159800Z

normal regex's are fairly simple and for the most part universal, so it's useful just to learn the syntax

em 2021-03-19T06:29:26.160400Z

but if you want to have programmatic capabilities or have something really complicated, you can check out https://github.com/lambdaisland/regal

cschep 2021-03-19T06:29:35.160700Z

in this case i’m passing in a search string

cschep 2021-03-19T06:29:37.160900Z

from a user

cschep 2021-03-19T06:29:54.161300Z

so if the user types “mike” i want to list all the names that contain “mike”

cschep 2021-03-19T06:30:17.161600Z

ended up with

(defn search-players [search]
  (filter #(re-find (re-pattern (str "(?i)" search)) (% :name)) (vals @league)))

em 2021-03-19T06:38:59.162200Z

yeah that'd work, but definitely consider not closing over mutable state and keeping that function pure

em 2021-03-19T06:39:08.162500Z

it doesn't need to know about league

em 2021-03-19T06:42:00.163500Z

one reason being testing, as you'd need to setup your state as well to test the function, whereas what the function is actually doing is just search, which can be pure

popeye 2021-03-19T14:05:24.167200Z

I have a property file <http://property.as|property.example >as below which is located in the my deployed server, How can I read the property file and fetch the data from :prod in key

{
	:dev  "id=dev;host=<http://127.0.0.1/update>"
	:prod "id=prod;host=<http://example.com/update>"
}

popeye 2021-03-19T14:05:40.167500Z

in clojure

Eamonn Sullivan 2021-03-19T14:50:12.167600Z

If I understand the question correctly, it would be something like:

(:prod (clojure.edn/read-string (slurp "location/of/property.example")))

tws 2021-03-19T14:52:37.168300Z

if you’re doing more substantial runtime configuration, perhaps you can use a library like https://github.com/juxt/aero. e.g.

{:webserver
  {:port #profile {:default 8000
                   :dev 8001
                   :test 8002}}}
You can specify the value of profile when you read the config.
(read-config "config.edn" {:profile :dev})

2021-03-19T15:13:39.169400Z

has anyone written or come across something like clojure.set/rename-keys-by (like rename-key, but takes a fn from key name to key name, rather than a map of remapped key names)?

alexmiller 2021-03-19T15:14:33.169700Z

several util libs have map-keys that can do this

alexmiller 2021-03-19T15:15:00.170200Z

(noting that a hash-map is a good function from name to name)

2021-03-19T15:16:21.170900Z

aha, I see that in medley.core, I think

alexmiller 2021-03-19T15:16:24.171Z

weavejester/medley, clj-commons/useful, etc

2021-03-19T15:16:40.171200Z

many thanks, Alex

alexmiller 2021-03-19T15:19:09.171700Z

we may add something like this to core too - feel free to vote at https://ask.clojure.org/index.php/1926/adding-functions-map-vals-and-map-keys !

👍 2
yiorgos 2021-03-19T20:06:23.174300Z

Is there an idiomatic way to set different environments (eg. production, testing…) with tools deps?

yiorgos 2021-03-19T20:06:38.174700Z

I would like to use a different db when I run my tests

seancorfield 2021-03-19T20:08:08.175300Z

@g3o There are a number of libraries folks use for that sort of thing but nothing specific to tools deps.

yiorgos 2021-03-19T20:08:48.176Z

I saw environ but it looks like is only for lein, at least from the Readme

seancorfield 2021-03-19T20:09:30.176500Z

https://github.com/juxt/aero is quite popular I gather, and not tied to Leiningen.

👍 1
yiorgos 2021-03-19T20:10:40.177300Z

sweet! I’ll have a look

yiorgos 2021-03-19T20:10:49.177600Z

thank you very much Sean!

az 2021-03-19T21:43:38.179300Z

Hi all, anyone here ever work with Emberjs? Would love any feedback on tips for getting productive in clojure coming from the ember world.

seancorfield 2021-03-19T21:46:40.180400Z

Hi @limix! Are you looking to learn Clojure on the JVM or ClojureScript for the browser?

az 2021-03-19T21:48:34.181300Z

Hi @seancorfield - Both, but I’m more curious on transitioning on the front end first. I’ve been working with re-frame now for a month. I’ve played with Fulcro for another month. I love everything so far, but I’m struggling to be as productive as I can be in Ember, mostly just the libs, addons all the things you get with a big framework like Ember.

az 2021-03-19T21:49:22.181500Z

Wondering if anyone from the Ember community has made the transition and how that went

seancorfield 2021-03-19T21:50:23.181700Z

I have a long history with Clojure but not much with ClojureScript so I find Fulcro pretty overwhelming. I’ve made more progress with re-frame: https://day8.github.io/re-frame/ but I’m not sure how it compares to the Ember ecosystem (you can use the whole React.js ecosystem with re-frame, as I understand it).

seancorfield 2021-03-19T21:52:07.182Z

I took an Ember.js workshop many, many years ago but I don’t remember much about it (I just looked at the website and it reminded me about the workshop — so parts of it are still familiar I guess).

az 2021-03-19T21:52:48.182200Z

Thank you

az 2021-03-19T21:53:39.182400Z

It might be easier to see if someone will share React -> Clojure experience as I’m sure it’s somewhat similar

1
az 2021-03-19T21:58:26.185700Z

To expand on my previous question, as Ember is relatively small, anyone coming from Angular or React? If so, any tips on getting productive quickly? The main point for us right now is moving from something with a ton of guard rails and convention, like rails, to what feels like something that we need to roll our own solution or patch together micro libs. I know it’s a pretty open ended question, but any thoughts are much appreciated.