Fork me on GitHub
#beginners
<
2016-10-24
>
rauh06:10:32

@credulous Replace the loop with a (fn [values] (let [...] (assoc ....)), then call it with (iterate the-fn v) and then you can take from it lazily

credulous09:10:52

Thank you kindly!

vandr0iy12:10:27

why am I unable to do this:

(case (class "randomstring")
   String (println "I'm a string")
   Long (println "I'm a long"))
?

rustam.gilaztdinov12:10:30

@vandr0iy why you use case? if should do what you want

(defn check-string [x]
  (if (instance? String x)
    "Yes"
    "No»))

vandr0iy12:10:03

it's an example, in my source I'm comparing a totally another object (I'm working on coda hale metrics). What I'm asking is: why am I unable to call 'case' on a class?

rustam.gilaztdinov12:10:22

may be this explain it? — https://clojuredocs.org/clojure.core/case

The test-constants are not evaluated. They must be compile-time
literals, and need not be quoted.  If the expression is equal to a
test-constant, the corresponding result-expr is returned. A single
default expression can follow the clauses, and its value will be
returned if no clause matches. If no default expression is provided
and no clause matches, an IllegalArgumentException is thrown. 

vandr0iy12:10:27

yeah, that might be it. I thought that String or Long represented the class directly, and wouldn't need any kind of evaluation... Thanks.

vandr0iy12:10:09

Probably for that I would need stuff like ^"[Ljava.lang.String;" , right?

rustam.gilaztdinov12:10:34

This is meta-info, I use this for type hint, like «pre-assert» in function. So, for you question, I think that’s doesn’t help 😞

vandr0iy12:10:34

sigh... i hate tranforming things just for the sake of comparison, but probably this is the case where I should stringify my (class my-value)... Thank you for your help!

chadhs13:10:18

is it considered idiomatic to have a function that would take more than 1 arg to instead take a map? so that way the order of arguments doesn’t become an issue?

chadhs13:10:02

i refactored an exercise from clojure from the ground up this way after encountering an order or args related error

chadhs13:10:11

have to say i like the idea/result

chadhs13:10:36

user> (first (most-prevalent-crimes {:data-file "2008.json" :crime :arson}))
{:report-count 42, :county "NV, Carson City", :prevalence 7.732528E-4, :county-population 54316}
user> (first (most-prevalent-crimes {:crime :arson :data-file "2008.json"}))
{:report-count 42, :county "NV, Carson City", :prevalence 7.732528E-4, :county-population 54316}

val_waeselynck13:10:36

@chadhs yes no problem, I do it a lot

val_waeselynck13:10:10

it's also a great way to pass around data or configuration without imposing knowledge on the intermediaries

chadhs13:10:28

cool thanks val!

chadhs13:10:22

i suppose then you’d want to make sure to put an example in the docstring so you don’t have to read the function to figure out what needs to go in arg-map or whatever one would call it

val_waeselynck14:10:43

@chadhs or use clojure.spec 🙂

val_waeselynck14:10:17

you can also (and you often will) perform destructuring in the arguments list, which will make the schema of the map more obvious

alexmiller14:10:41

@chadhs one way this is commonly done is with “kwargs” - a function defined like (defn most-prevalent-crimes [& opts] (let [{:keys [data-file crime]} opts] …))

alexmiller14:10:27

opts is a sequence of key-value options but is destructured as a map

alexmiller14:10:49

you would invoke that like (most-prevalent-crimes :data-file “2008.json” :crime :arson)

alexmiller14:10:32

because you are destructuring to an (unordered) map, you can pass the options in any order

sveri14:10:55

As an addition, what I usually do, is separate parameters by type, where one type is the data passed around and another type could be the database connection or another component that I might be using

chadhs14:10:59

@val_waeselynck i’ve yet to dive in the pool, but it’s on my list.

alexmiller14:10:17

this is primarily used for options where some can be omitted and is a useful technique for the outer layers of an API

chadhs14:10:18

@alexmiller super helpful; i’ll do a quick refactor; thank you 🙂

alexmiller14:10:56

but is often discouraged for internal layers, where it’s better to just pass a map - this ends up being easier to pass through other downstream functions than reassembling the flattened opts

chadhs14:10:13

that makes sense (re: optional args / api usage)

alexmiller14:10:32

the other thing it enables is using a destructuring :or clause in the map destructuring to provide defaults

chadhs14:10:56

oh… nice

alexmiller14:10:27

(defn most-prevalent-crimes [& {:keys [data-file crime] :or {crime :arson}}] … )

alexmiller14:10:46

so that makes :arson the default :crime and you can omit that option

alexmiller14:10:55

when calling most-prevalent-crimes

alexmiller14:10:19

if you’re using spec, it has support for spec’ing this kind of function too with keys*

chadhs14:10:51

thnx for the quick examples along side the reasoning 👍:skin-tone-3:

chadhs14:10:55

i’ve not used spec yet; but i did just listen to the episode of defn you were on so on my list to try soon @alexmiller 🙂

alexmiller14:10:22

(s/def ::crime #{:arson :theft})
(s/def ::data-file string?)
(s/fdef most-prevalent-crimes
  :args (s/keys* :req-un [::crime ::data-file])
  :ret (s/coll-of ::info))

alexmiller14:10:50

start of a spec for that fn above (obviously incomplete)

chadhs14:10:56

i capturing all this in OmniFocus to review later when I have more time to play so all very helpful; thank you

alexmiller14:10:59

@vandr0iy Java classes are not constants and can’t be used as case options - they must be loaded by a classloader and there can be multiple ones in play (one per classloader) in a JVM

alexmiller14:10:05

@vandr0iy if you are looking to dispatch based on type, protocols are the fastest way to do so in Clojure (as they leverage the highly optimized JVM machinery for virtual dispatch in the JVM)