Fork me on GitHub
#beginners
<
2018-03-27
>
noman01:03:13

hey guys how come clojure is so terse? like all the variable names are extremely truncated. (let [[f & rs] undo-values] instead of first and rest? It seems to be almost the exact opposite of Java style and many other languages

Michael Fiano02:03:55

@noman those are variables and not defined by the language.

Michael Fiano02:03:24

Also you don't want to shadow builtins with variable names for a few reasons.

noman02:03:49

i know they're defined by people but it seems all the clojure code i read does this, it seems like idiomatic clojure style

Michael Fiano02:03:57

Clojure is a Lisp-1 and therefor variable and function names are in the same scope. Using first and rest here would prevent the use of those functions, and also with a proper editor, color your variable names differently to highlight that to the programmer.

noman02:03:12

ah yeah that was a bad example i guess

noman02:03:17

since those are also functions

noman02:03:42

but maybe that's why? cuz all the words are used up by functions so you have to get creative with naming?

Michael Fiano02:03:00

Are you coming from Common Lisp?

noman02:03:11

no, i'm very new to lisps

dpsutton02:03:12

norman, i'm with you. my variable names can get descriptive and quite long

Michael Fiano02:03:16

Common Lisp is known to use very explciit variable names conventionally

dpsutton02:03:18

set your own style

noman02:03:26

clojure is my first foray into functional programming outside of functional javascript and functional java styles

noman02:03:51

@dpsutton i know, i know, it's just nice to see what smart people are doing at first, to emulate them

Michael Fiano02:03:51

Oh well a fine choice then. Just let this fact slip by you and continue your learning. You'll understand a bit more later.

Michael Fiano02:03:47

But it's always a good idea to write short functions that do one thing well, and use explicit variable names for self-documenting code. That's not to say documentation isn't a good thing though šŸ˜‰

noman02:03:15

okay, i actually love writing descriptive variable names so it's nice to know that cryptic variable names is not a clojure convention

Michael Fiano02:03:10

There are a few conventions in this regard though

Michael Fiano02:03:31

For example, using colland pred to denote a collection or predicate, respectively, is a convention used by some. This is because the signatures of the language itself do this for brevity, and Clojure programmers will have a better idea knowing what you mean if you stick to style conventions.

noman02:03:00

yeah okay, that makes sense

Michael Fiano02:03:09

Google for Clojure style conventions. There's a few links with some overlap...take what you will from them all

noman02:03:14

cool, thank you

seancorfield03:03:48

There are some short symbol names that are fairly idiomatic but my advice is to use names that are clear and expressive. Read Zach Tellman's "Elements of Clojure" (ebook) for some really good advice about naming.

seancorfield03:03:59

You will, however, see f and g for general function arguments (i.e., arguments that are functions). m for a general hash map, l for a general list, s for a string, i or n for a number (int). And x for a generic "thing", with xs for a sequence/collection of x. @noman

seancorfield03:03:50

The more high-level and abstract and generic code is, the harder it is to give arguments "sensible" names.

seancorfield03:03:43

You'll find examples of these very generic short names in the docstrings for core functions, e.g.,

user=> (doc comp)
-------------------------
clojure.core/comp
([] [f] [f g] [f g & fs])
  Takes a set of functions and returns a fn that is the composition
  of those fns.  The returned fn takes a variable number of args,
  applies the rightmost of fns to the args, the next
  fn (right-to-left) to the result, etc.

seancorfield03:03:20

This shows the x and xs convention -- for f.

Alex Miller (Clojure team)03:03:16

note that these conventions really apply to generic functions like you find in core

Alex Miller (Clojure team)03:03:40

the bulk of your code is not generic - itā€™s dealing with very specific things pertinent to your business

Alex Miller (Clojure team)03:03:53

so youā€™re far more likely to have (defn active-contracts [db user]) or whatever

donaldball03:03:35

A pithy maxim Iā€™ve heard is: the broader the type, the shorter the symbol

4
šŸ’Æ 4
noman03:03:16

Wow thanks for the awesome explanations. That makes a lot of sense and also helps decipher the docstrings I've been reading

mbjarland09:03:42

I have a design question. This is half ā€œfor realā€ and half a thought experiment and a journey in learning clojure. Iā€™m building a cli application in clojure and am trying to make the implementation extensible in various dimensions. One of the dimensions is the types of files the cli application should be capable of searching through. My initial implementation uses multimethods with something like:

(defmulti find-in-file-type
          (fn [^File f _ _ _] (file-ext f)))

(defmethod find-in-file-type "jar"
  [^File f display-name handler opts]
  (...implementation))

(defmethod find-in-file-type "zip"
  [^File f display-name handler opts]
  (...implementation))
and since these different file types are supposed to be 1. added to 2. enabled/disabled from the cli I had the bright (and totally incorrect it turns out) idea of using function metadata for the defmethod definitions above to describe the cli behaviour (say whether this file type is searched by default and what the cli help and options should call this file type etc). Turns out meta data for defmethod is a no go. My thought there was that a person adding a new file type would be able to just define another multimethod variant with some metadata and the cli parser would understand the meta and take care of the rest. I could have called (methods find-in-file-type) and friends to introspect the types and produce correct command line help etc. What would be a good way to model this as it seems that meta data on multi methods was not a good idea/not available?

mbjarland09:03:37

The way I understand it there is metadata on the defmulti but not on the defmethod which would invalidate my idea of using the meta for extending the cli interface

joelsanchez10:03:24

you can just return a function with meta, from your methods

joelsanchez10:03:51

(defmethod find-in-file-type "jar"
  [f display-name handler opts]
  ^{:options []
    :search-by-default? true}
  (fn []
    (do-things-here f display-name)))

joelsanchez10:03:10

obviously you need to call it, but you have the meta and the implementation, this way

joelsanchez10:03:24

another option is to just return a map from your methods, like {:fn ... :help ... :search-by-default? ...}

mbjarland10:03:13

:man-facepalming: ok, that makes total sense. Guess Iā€™m still getting used to the fu

joelsanchez10:03:08

lastly, multimethods are not the only way to extend programs...you can use a private atom and define your own function for adding methods to it, and then your own dispatch function

mbjarland10:03:00

@joelsanchez sounds reasonable. Many thanks for the reality check. I think Iā€™ll stick with multimethods for this round as a learning exerciseā€¦unless there is a specific reason no to?

joelsanchez10:03:21

no, no specific reason, just mentioning the possibility šŸ™‚

mbjarland10:03:00

I guess the atom approach would hide more of the implementation details and leave more freedom to do wheatever you want to internally

mbjarland10:03:07

I guess both approaches essentially use global state, the atom becauseā€¦well itā€™s an atom, and multimethods in a more intrinsic way

joelsanchez10:03:07

just a style / personal preference thing imo šŸ™‚ think carefully!

mbjarland10:03:59

@joelsanchez many thanks, this was exactly the kind of answer I was looking for

šŸ‘ 4
joelsanchez10:03:16

by the way, defmethod does this internally, in -add-method: (swap! method-table assoc dispatch-val method)

mbjarland10:03:34

ha! ok, didnā€™t know that

timo10:03:43

hi there, I want to use react-leaflet and I need a way to learn about the interop of js-react with cljs-reagent. Anyone has some good resource on this?

timo13:03:17

that was what I was confused about: :> found an explanation at https://reagent-project.github.io/news/news060-alpha.html

Michael Fiano11:03:22

Someone here recommended me a good testing library and I forgot what it was. What's a good one?

hawari11:03:44

Is it midje?

Michael Fiano11:03:48

That wasn't it

Michael Fiano11:03:28

Ah nevermind. It was midje. Thanks.

pooboy12:03:21

guys,can you all suggest a very light ftamework for clojure

pooboy12:03:26

Like sinatra

Josh Horwitz12:03:25

Http-kit with a routing library?

Mike C13:03:38

So Iā€™m just getting started with ClojureScript / Om.next / React. Iā€™ve got a basic component up and running with a reconciler etc. Are there any popular choices for a library that handles hooking into the reconciler and talking to a server to request and synchronise data, or is that a roll-your-own thing?

sundarj22:03:09

with Om Next it's a roll your own thing; with Fulcro networking is provided for you

mbjarland13:03:33

@pooelfbin I would have looked under ā€œHTTP Routingā€ or ā€œWeb Frameworksā€ at https://www.clojure-toolbox.com/. One option would be for example compojure where you can start a compojure project (assuming you have leiningen installed) with:

~> lein new compojure myproject
...
~> cd myproject
~> lein ring server-headless
(compojure is a routing library as suggested by @joshua.d.horwitz above)

feihong13:03:26

I've seen a lot of code samples where in the requires section they put [clojure.string :as str]. What if you need to use the actual str function? Do you just have to write clojure.core/str?

lispyclouds13:03:40

@feihong.hsu (str arg) should work as it is. Functions from clojure.string like (str/split arg) can be used too. The clojure runtime differentiates correctly based on how you call it.

frenata13:03:45

The namespace alias isn't itself a valid symbol:

(require '[clojure.string :as stringy])
=> nil
stringy
=> CompilerException ...
So str as above is unambiguous.

timo14:03:17

hi folks, how do you spec a Long?

Alex Miller (Clojure team)14:03:39

well, that will match any fixed precision integer

Alex Miller (Clojure team)14:03:52

if you really want just a long, then #(instance? Long %)

šŸ‘ 4
timo14:03:41

thanks! didn't realize that I can use instance? Long

timo14:03:47

ok int? is fine as well because clojure is using long as ints?!? got it!

Fahd El Mazouni14:03:54

Hello brothers and sisters !

4
lum14:03:04

hi, there's this function, that I don't understand yet. For such functions, how do you try to understand them? Do you go step by step? Is there such a debugger or similar tool? Here's the function:

lum14:03:09

(defn permutations [a-set]
             (if (empty? a-set)
               (list ())
               (apply concat 
                      (map (fn [x] (map cons (repeat (first x)) (permutations (rest x))))
                           (rotations a-set)))))

joelsanchez14:03:23

that's not very idiomatic code tbh

lum14:03:53

I'm trying to make a simpler exercise with the repeat to try to understand it

sooheon14:03:12

@lum Whatā€™s the function rotations?

sooheon14:03:35

arenā€™t sets orderless?

lum14:03:13

(defn tails [a-seq]
  (if (empty? a-seq)
    (cons a-seq ())
    (cons (map (fn [x] x)  a-seq) (tails  (rest a-seq)))))

(defn inits [a-seq]
  (if (empty? a-seq)
    (cons a-seq ())
    (cons (reverse  (map (fn [x] x) (reverse a-seq))) (inits (reverse (rest (reverse a-seq)))) )))

(defn rotations [a-seq]
  (if (empty? a-seq)
    '(())
    (rest (map concat (tails a-seq) (reverse (inits a-seq))))))

dpsutton14:03:06

This is scheme code so sets are lists with an invariant

lum14:03:29

@sooheon The order is not important.

sooheon14:03:45

quick plug for my question at #code-reviews, btw

lum14:03:01

That repeat means its argument (first x) keeps getting cons until there are arguments for the inner map, correct?

sooheon14:03:37

until there are no more arguments, yeah

sooheon14:03:35

although I donā€™t think the repeat means that. The repeat means thereā€™s an endless lazy sequence of (first x) lined up as the first arg to map.

lum14:03:55

@joelsanchez so in a more idiomatic way what changes would you propose?

joelsanchez14:03:20

sorry I'm at work but 1) use sets 2) use mapcat 3) most likely this is done better without cons (zipmap?)

lum14:03:12

Thanks, I'll try an alternative

hawari15:03:15

Is "Joy of Clojure" considered as an intermediate level book? I'm looking for a continuation for "Clojure for the Brave and True"

jumar15:03:22

Intermediate-to-advanced I'd say. Perhaps it's good to read a couple of other books before Joy.

hawari15:03:13

What would you recommend beside "Clojure for the Brave and True" as a prelude?

schmee16:03:58

:thumbsup: for Joy of Clojure, if you've read Brave and true and played around some with Clojure it's a perfect fit IMO

šŸ‘ 4
jumar08:03:48

I think Alex's Clojure Applied is great as well as Getting Clojure (more beginner-like but some really good stuff there) and Elements of Clojure.

Alex Miller (Clojure team)15:03:52

Joy of Clojure is a great choice. Iā€™d also humbly suggest Clojure Applied (I am a co-author).

hawari15:03:47

Alright, I'll check it out as well then

timo15:03:13

how do you guys filter vectors of nested maps? doseq?

timo15:03:52

[{:customer1 {:address1 {:lat :lon :contact} :address2 {:lat :lon :contact}}} {:customer2 ...}]

timo15:03:52

say I want to save :contact in a new vector

danm15:03:21

Should there be a map around each :customer, or is that really a vector where it alternates between keys and values (poke it with (into {})?)?

timo15:03:37

no sorry

dpsutton15:03:41

can you give example input and output? not super clear on what you're looking to accomplish

timo15:03:42

yeah you're right...it was not really good...I will try better...give me a sec

agigao15:03:51

Hi Clojurians, anyone experienced interested in making a quick comparison of - duct, pedestal, luminus? Thanks :)

dpsutton15:03:12

this is a sequence of maps. do you know how to get that information off of one of these maps? You know how to access map entries right?

dpsutton15:03:26

not being sarcastic, just not sure where your comfort with clojure is

timo15:03:13

I know how to destructure but the looping while destructuring is kind of difficult for me

dpsutton15:03:57

make a function that destructures and makes a single object that you want. and then map that over your collection

4
timo15:03:33

alright I guess it's easy and I am just not thinking it straight

danm15:03:34

This is horribly hacked quickly and there's probably a nicer way to build the map per customer, but:

(defn extract-details [customer]
  {:slug (:slug customer)
   :dropoff-address {:lat (get-in customer [:dropoff-address :point :lat])
                     :lng (get-in customer [:dropoff-address :point :lng])}
   :pickup-address {:lat (get-in customer [:pickup-address :point :lat])
                    :lng (get-in client [:pickup-address :point :lng])}})

(map extract-details customers)

andre.stylianos16:03:53

:point already contains :lat and :lng so extract-details can be simplified to

(defn extract-details [customer]
  {:slug (:slug customer)
   :dropoff-address (get-in customer [:dropoff-address :point])
   :pickup-address (get-in customer [:pickup-address :point])})

andre.stylianos16:03:26

Basically it maps over each element in the vector and creates a new vector where each element is the result of passing a customer to extract-details

dpsutton16:03:39

always ask questions. happy to help! did we solve your issue? i hope i didn't make it sound like this was an "easy" thing

dpsutton16:03:00

it takes a bit to get away from loops like other languages

danm16:03:32

@U485ZRA58 Of course. For some reason I was convinced :point had more than just those 2 keys in it... Don't know why

šŸ˜› 4
andre.stylianos16:03:17

@timok, not sure if you've seen the threaded responses ā˜ļø

danm16:03:40

For thread-macro fun you can also do

(defn extract-details [customer]
  (-> customer
      (select-keys [:slug :dropoff-address :pickup-address])
      (update :dropoff-address :point)
      (update :pickup-address :point)))
However while that is less characters it's also probably less clear what exactly it's doing if you're first coming to it or coming back to it after some time away and don't quite remember the data structure, and I'm a fan of clarity šŸ˜‰

danm16:03:19

Even if it means being more verbose. As long as it's not problematically slower šŸ˜‰

timo15:03:42

thanks anyway

Russ Olsen15:03:17

@timok When you are thinking you need a loop in another language there's a fair chance you need a call to map in Clojure. Think of map as taking the place of all those <collection> -> <transformed collection> loops.

timo15:03:12

@russ767 I think this is exactly my problem, thanks!

tdantas15:03:48

hey, good morning guys

suppose I have 3 channels with a lot of activity
everytime I hit the `alts!!`  I'm going to create a new timeout channel, right ?

is that the correct way to do ? the follow line will create tons of timeouts that will never be used . 
am I wasting resource here ? how can I avoid that ?

(future
 (while true
  (let [[value channel] (alts!! [c1 c2 c3 (timeout 10000)])]
    (some-computation-with-value value))))

tdantas15:03:35

any way to cancel de timeout ( something like js clearTimeout)

donaldball16:03:05

I donā€™t think there is, but there is already a single thread dedicated to closing timeout channels when their (approximate) period is elapsed. Timeout channels are fairly lightweight.

donaldball16:03:45

You can close the timeout channel yourself, but I donā€™t think itā€™s any cheaper than letting it close itself.

donaldball16:03:28

Iā€™ve certainly written code much like this before and it seems eminently reasonable to me, assuming you do in fact want to do Something every 10 seconds even if you havenā€™t received a value on your other channels.

tdantas16:03:26

> when their (approximate) period is elapsed assuming the other channels we have a lot of activity. every 10ms Iā€™m receiving one message. the loop will create every 10ms one timeout channel with 10s timeout

tdantas16:03:09

100 timeout channels are going to be created in just one second. 1000 timeout channels in 10 seconds that rarely are going to be used

andre.stylianos16:03:17

@timok, not sure if you've seen the threaded responses ā˜ļø

donaldball16:03:29

You could write an atom-based timeout-style channel whose state updates with the last message received time, but honestly it seems like itā€™d be about as much work as the provided timeout channel currently takes. But when in doubt, benchmark!

tdantas16:03:08

yeah @donaldball I will benchmark .. thx

donaldball16:03:06

How precise do you need to be about your 10s with no activity trigger?

tdantas18:03:35

not much, any other idea ?

donaldball15:03:55

Sure. In an atom or volatile, you could record the time you last received/processed a real message, and you could have a single wakeup channel that received a signal every e.g. x seconds. When it wakes up, it checks the time difference between now and the last real message received and if greater than 10 seconds, does whatever that thing is.

tdantas15:03:55

sure . exactly what I did. Iā€™ve created one thread that will sleep for X seconds and wakeup and send a signal to one channel

oVerde17:03:03

Any Google App Engine experiences here?

Drew Verlee18:03:59

whats the ideal way to combine several vectors into one?

(= [1 2] (???? [[1] [2]])) 
=> true
i think reduce concat seems ok, but it feels ok. curiuos what other options there are.

reborg20:03:35

(into [] cat [[1] [2] [3]]) is the fast option

jcr18:03:10

@drewverlee you can use into if you need a vec instead of a seq

ghadi18:03:53

concat will blow up

ghadi18:03:52

Because it's lazy

dpsutton18:03:55

Do you like reduce into [] instead?

ghadi18:03:11

That works

Drew Verlee18:03:35

interesting. ill read over that

OctarineSorcerer19:03:29

Is it possible to build up a vector from another vector? Map wouldn't work amazingly smoothly, as it would produce [a [nested thing]]

OctarineSorcerer19:03:02

Roughly speaking, am taking what's in :arglist metadata (so [a b & {:keys [c d]}] or similar)

OctarineSorcerer19:03:35

And wanting to turn it into a b c d

OctarineSorcerer19:03:47

In the least painful way possible

madstap19:03:10

@daniel.gomme An argvector can be arbitrarily nested. Do you want to take an arbitrarily nested collection and return a flat vector of the symbols inside?

OctarineSorcerer19:03:46

Oh damn, didn't realise that about argvectors. Yeah, I pretty much just want to extract all the symbols within an argvector out into one vector

noisesmith19:03:32

something like (filter symbol? (flatten argvec)) maybe? usually flatten is a terrible choice but it kind of makes sense there

noisesmith19:03:16

filterv if you really need vector and not lazy-seq

noisesmith19:03:11

oh wait, flatten ignores hash-maps, so itā€™s more complex than that

madstap19:03:23

My util library has a function for this. You'd use it like (comfy/prewalk-transduce (filter symbol?) conj argvec)

OctarineSorcerer19:03:27

I'll have a look! Though I'd like to find out the nice way of doing this in vanilla, still getting to grips with the language

madstap20:03:25

Probably not a bad idea, though I don't think there's like an easy one-liner for this in the stdlib. Maybe try to do a recursive solution with a reduce.

Michael Fiano21:03:17

What is the convention for naming function parameters of a HOF in the case of more than one function parameter?

arrdem21:03:16

Depends on the context. For a general HOF, I agree that f, g, h and successors are appropriate, but the terse names only make sense when you actually make no (or very few) assumptions about the functions save that they are functions. If have more constraints, Iā€™ve used the -fn suffix eg test-fn or generator-fn to try and communicate that intent.

danielo51522:03:50

Hello everyone

danielo51522:03:22

I'm struggling to run a cljs repl with fighweel on node

danielo51522:03:01

Well, actually I finally made it, but I am a bit confused about how to load namespaces

edward22:03:39

How can I see the expansion of a macro I define in clojurescript? For example I do (from https://cljs.github.io/api/cljs.core/defmacro):

myproject.core> (defmacro str->int [s]
  `(js/parseInt s))
#'myproject.core/str->int
but when I do a macroexpand I get
myproject.core> (macroexpand '(str->int "foo"))
(str->int "foo")
Doing a macroexpand of a built-in macro works fine (from https://cljs.github.io/api/cljs.core/macroexpand):
myproject.core> (macroexpand '(when true :foo))
(if true (do :foo))
Here are the specs for my REPL:
;; Connected to nREPL server - 
;; CIDER 0.17.0snapshot (package: 20180203.535), nREPL 0.2.13
;; Clojure 1.8.0, Java 1.8.0_112
Thanks!