Fork me on GitHub
#beginners
<
2021-03-19
>
Michael Lan02:03:37

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

andy.fingerhut02:03:31

Anonymous functions are definitely more flexible.

andy.fingerhut02:03:02

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

andy.fingerhut02:03:25

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

hiredman02:03:19

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

hiredman02:03:33

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

seancorfield03:03:28

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 Lan03:03:23

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

noisesmith16:03:15

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

seancorfield16:03:46

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.

cschep04:03:44

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

cschep04:03:13

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

cschep04:03:16

in the first map

cschep04:03:30

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

cschep04:03:43

any pointers for the clojure take?

cschep04:03:59

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

cschep04:03:04

feels .. wrong

cschep04:03:19

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

cschep04:03:12

oo maybe doseq

dpsutton04:03:21

(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}]}}

cschep04:03:22

what’s fnil?

dpsutton04:03:00

check out (doc fnil)

cschep04:03:23

oh that’s cool

cschep04:03:44

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

cschep04:03:46

but that’s probably the wrong idea

dpsutton04:03:50

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

dpsutton05:03:27

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

dpsutton05:03:38

and in that sense you can't update the original

cschep05:03:44

right, right

dpsutton05:03:19

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)

cschep05:03:07

ok thanks!

cschep05:03:11

a bit to chew on here

seancorfield05:03:23

@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).

cschep05:03:00

yeah, totally

cschep05:03:05

;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))

cschep05:03:19

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

cschep05:03:50

but it does “work”

seancorfield05:03:30

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.

robertfw02:03:20

@U04V70XH6 what do you use to generate this summary?

seancorfield02:03:05

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

cschep05:03:14

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

andy.fingerhut05:03:23

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

cschep05:03:37

so there should just be the one

cschep05:03:44

make json out of it and return it from web routes

cschep05:03:49

and update it by getting posts

cschep05:03:16

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

seancorfield05:03:07

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

cschep05:03:09

spitting it to a flat file

cschep05:03:15

voila, persistence!

seancorfield05:03:15

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 🙂

cschep05:03:40

ha ha, ok awesome

cschep05:03:27

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

cschep05:03:31

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

dpsutton05:03:03

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

😲 4
cschep05:03:35

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

cschep05:03:38

for 8 hours a day

cschep05:03:40

not gonna work

seancorfield05:03:18

Easy: I never write JS 🙂

seancorfield05:03:00

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

clojure-spin 6
cschep05:03:16

what were you writing before?

seancorfield05:03:55

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

seancorfield05:03:19

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

cschep05:03:49

sounds like you landed in a good spot

cschep05:03:50

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

seancorfield05:03:05

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

cschep05:03:10

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

cschep05:03:50

.contains doesn’t seem to work

seancorfield05:03:50

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

cschep05:03:58

ahh ok i’m glad i’m not crazy

cschep05:03:23

#(.contains % %1) like that?

seancorfield05:03:38

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.

cschep05:03:46

ohhh that will probably work

seancorfield05:03:41

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.

seancorfield05:03:04

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

cschep06:03:38

oh yes, how to programmatically construct a regex?

cschep06:03:07

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

cschep06:03:11

can you string interpolate into them?

cschep06:03:48

oh.. re-pattern

em06:03:12

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

em06:03:26

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

cschep06:03:35

in this case i’m passing in a search string

cschep06:03:37

from a user

cschep06:03:54

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

cschep06:03:17

ended up with

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

em06:03:59

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

em06:03:08

it doesn't need to know about league

em06:03:00

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

popeye14:03:24

I have a property file >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="
	:prod "id=prod;host="
}

Eamonn Sullivan14:03:12

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

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

popeye14:03:40

in clojure

tws14:03:37

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})

Jeff Evans15:03:39

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)?

alexmiller15:03:33

several util libs have map-keys that can do this

alexmiller15:03:00

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

Jeff Evans15:03:21

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

alexmiller15:03:24

weavejester/medley, clj-commons/useful, etc

Jeff Evans15:03:40

many thanks, Alex

alexmiller15:03:09

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 !

👍 7
yiorgos20:03:23

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

yiorgos20:03:38

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

seancorfield20:03:08

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

yiorgos20:03:48

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

seancorfield20:03:30

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

👍 3
yiorgos20:03:40

sweet! I’ll have a look

yiorgos20:03:49

thank you very much Sean!

az21:03:38

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

seancorfield21:03:40

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

az21:03:34

Hi @U04V70XH6 - 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.

az21:03:22

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

seancorfield21:03:23

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).

seancorfield21:03:07

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).

az21:03:48

Thank you

az21:03:39

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

3
blak3mill3r04:03:23

I used Angular.js in the time before React existed, and used plain reagent in the time before re-frame existed. FWIW, I would also suggest kicking the tires of re-frame. It seems like just enough structure: not a million tiny pieces for you to wire together, and not an opinionated & monolithic framework.

👍 3
az21:03:26

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.