Fork me on GitHub
#beginners
<
2018-09-21
>
diego.vid.eco01:09:42

what is the most idiomatic way to conditionally call a function?

seancorfield01:09:38

@diego.vid.eco Can you provide a bit more context?

diego.vid.eco01:09:24

something like (if something call-this else-call-this)

diego.vid.eco01:09:42

perhaps with different arguments

seancorfield01:09:02

Sounds like if is what you want then.

seancorfield01:09:26

(which is why it seemed to be a confusing question)

seancorfield01:09:43

Is there a reason you think (if something call-this else-call-this) might not be idiomatic @diego.vid.eco?

diego.vid.eco01:09:27

how do I pass the arguments?

seancorfield01:09:00

If they have different arguments

(if something
  (call-this with its args)
  (else-call-this with different argument values))

diego.vid.eco01:09:27

ok, in the docs I did not see any examples calling functions

seancorfield01:09:50

Function calls are just expressions like everything else.

seancorfield01:09:21

If you had two functions that you wanted to chose between but call with the exact same arguments, you might do this instead

(let [f (if something a-function another-function)]
  (f some argument values))

seancorfield01:09:52

Does that help?

diego.vid.eco02:09:46

thanks @seancorfield

ejstembler03:09:39

Quick question… In the project.clj file what is the :url value for? For example: :url "". Can I put my git repo url there, or my blog post url there? Or does it expect something else?

alexmiller03:09:17

whatever the “home page” is for your project, so github is probably best for a lib

ejstembler03:09:00

@alexmiller Okay thanks!

hoertlehner05:09:09

Is there a way to know in which order the repl loads the clojure files when it gets started? I am using proto-repl, and am getting a strange startup error ":error-while-loading no.en.core Could not locate no/en/core__init.class or no/en/core.clj on classpath." I do not try to load a "no/en" namespace anywhere in my project.

hoertlehner06:09:05

I guess this is the order it processes the files. But I do neither have a "no.en.core", nor a "user" namespace in my src folder.

seancorfield06:09:31

user is just the default namespace for a REPL.

seancorfield06:09:16

FWIW, I turn OFF the three reload options for proto-repl -- I find them far more trouble than they're worth. That will probably stop this issue.

seancorfield06:09:10

no.en.core looks to be coming from this library https://github.com/r0man/noencore

seancorfield06:09:22

Hard to tell why that's failing to load -- it's probably a transitive dependency of something else you're depending on.

hoertlehner09:09:48

thanks @seancorfield

seancorfield06:09:34

^ @hoertlehner

seancorfield06:09:53

(there's a lot of magic in thi.ng.strf.core as well that I would want to avoid)

peter.kehl06:09:44

Hi. Why is (contains? [:a] :a) false, please? But a keyword in a vector seems to stay as that keyword: (= ([:a] 0) :a) is true...

seancorfield06:09:37

contains? is probably one of the most misunderstood Clojure functions. It checks associative collections for the presence of a "key". Vectors are associative on their index, not their contents.

seancorfield06:09:03

You'll see that (contains? [:a] 0) is true while (contains? [:a] 1) is false -- because the "keys" in the vector [:a] are just 0 (there is no 1-th element).

seancorfield06:09:14

Does that help @peter.kehl?

cr06:09:48

an alternative would be to use some? iirc

peter.kehl06:09:30

Ah, I forgot.... Thank you.

peter.kehl06:09:14

(I have invoked (doc contains?) but I haven't read it properly...)

seancorfield06:09:27

@cr No, some? is arity 1 and just tests whether its argument is nil or not.

cr06:09:39

yes, sorry, some

cr06:09:42

just looked it up

seancorfield06:09:11

Nope, that takes a predicate and a collection and won't help you with a vector of keywords either.

seancorfield06:09:45

(some (partial = :a) [:a]) would work tho'

seancorfield07:09:36

(i.e., you can't do (some [:a] :a) or (some :a [:a]) )

cr07:09:06

i would have used (some #(= :a %) [:a :b :c])

cr07:09:41

not sure if this is idiomatic though, i'm beginner myself

timo.freiberg07:09:41

How about (some #{:a} [:a :b :c])

cr07:09:18

i like that 🙂

seancorfield07:09:59

Yes, that's another possibility.

timo.freiberg07:09:20

Using sets as functions is not universally popular though

seancorfield07:09:24

(but you get :a rather than true)

seancorfield07:09:57

Given the way spec is leading people, I think sets-as-predicates will become more popular/idiomatic.

peter.kehl07:09:09

Thanks everyone. And to @timo.freiberg for some with a set as a predicate. That's what I needed, along with returning :a instead of true - thanks @seancorfield for point it out.

denisgrebennicov11:09:27

Is Lisp (and therefore Clojure as well) a multi-stage programming language?

orestis11:09:28

Is the definition of multi-stage programming this: https://en.wikipedia.org/wiki/Multi-stage_programming ?

denisgrebennicov11:09:14

yeap runtime code gen is possible due to quotations and executing that code is possible due to eval, right

mbjarland13:09:20

@mfikes, @coinedtalk or whoever might be interested, for the english list function discussed a couple of days back, if we can do away with the oxford comma, we can also write:

(defn english-list [[f s & _ :as xs]]
  (if (not s)
    f
    (apply str (concat (interpose ", " (butlast xs)) [" and " (last xs)]))))

mbjarland13:09:28

not that I got stuck and obsessed over this or anything

veix.q514:09:13

what is the difference between a Thread. and a core.async/thread ?

alexmiller14:09:53

thread is a function that spins up a Thread and runs your function on it, returning the result on a channel

borkdude14:09:28

@veix.q5 there’s also future

alexmiller14:09:02

thread is intended to be mostly “like future”, but returns the result on a channel rather than as a value

henrik16:09:05

(<!! (a/thread (inc 1)))
@(future (inc 1))
Nearly identical semantics, though for simple stuff @ and future is handy.

peter.kehl16:09:34

Why does (doc empty?) suggest: Please use the idiom (seq x) rather than (not (empty? x))? Yes, (not (empty? x)) is a little less efficient, because (source empty?) shows (empty? x) implemented as (not (seq coll))). However, (not (empty? x)) tells my intent, but (seq x) hides it (less obvious => more mental energy wasted when reading the code later). If you used Clojure for a while, do you really read that usage of (seq x) naturally?

henrik16:09:21

@peter.kehl To me, it feels a bit clumsy. One is a question and the other a statement? I don’t get it. Regardless, I have learned to read it just by encountering it repeatedly, and do remember to use it when I feel like negating empty?. I sometimes catch myself inverting code to avoid seq in favor of empty? though.

henrik16:09:26

I suppose (def not-empty? seq) is an option if you find it impossible to swallow, but I’m not sure it’s good form.

noisesmith16:09:18

there's also not-empty whcih returns the original object, or nil if it was empty, for any seqable? type

mfikes16:09:16

@peter.kehl Yeah, using seq to mean not empty is truly a preferred idiom, unfortunately not as clear, but, alas, that's what it is.

peter.kehl17:09:04

Thank you @henrik, @noisesmith and @mfikes.

peter.kehl17:09:43

Is there any macro or special form to prevent re-binding symbols? Ideally an alternative to let that would warn/fail if re-binding an already bound symbol (or if re-binding an existing variable/function), please?

mfikes17:09:07

None that I recall in the standard lib

noisesmith17:09:40

there's defonce which silently does nothing if the var already exists

noisesmith17:09:14

(according to @gfredericks , defonce rhymes with Beyonce)

andy.fingerhut17:09:46

@peter.kehl If your reason for asking is to detect mistakes in your code like using a let to rebind the value of map, forgetting you did that, and then calling the locally bound map inside the let form hoping to get clojure.core/map, but instead getting the local one, Eastwood can warn about such shadowing. It isn't always an error to do that, but it looks suspicious. https://github.com/jonase/eastwood

noisesmith17:09:15

eastwood doesn't warn about using the same let binding twice in one block though (and I'd argue it shouldn't because it's a common pattern to do so intentionally)

andrew35417:09:31

@peter.kehl I've slowly gotten used to reading (seq xs) naturally. I had to sort of grin and bear it at first.

lockdown-18:09:45

yes, coming from scheme looks weird at first

denisgrebennicov18:09:30

is there any simple implementation of disjoint-set data structure in clojure? this one is just too much https://github.com/jordanlewis/data.union-find/blob/0.1.0/src/jordanlewis/data/union_find.clj

andy.fingerhut19:09:58

@noisesmith Yes, Eastwood's warning for such code tries to catch the thing that is most often an error, without warning about common usage patterns (even if those common usage patterns might also be bugs).

andy.fingerhut19:09:17

@denisgrebennicov I haven't used any of them, but a Google search of "clojure union find" (without the quotes) turns up several libraries and a blog post or two. Some of them may be simpler than the one you linked.

peter.kehl19:09:07

https://clojuredocs.org/clojure.core/type reads:

;; Checking a macro
user=> (type fn)
user$fn

user=> (type clojure.core/fn)
java.lang.Exception: Can't take value of a macro...
However, in clj 1.9.0 both expressions fail (though not with java.lang.Exception but with java.lang.RuntimeException) with the same error description. Can that be a mistake in the doc, or did the implementation of fn change from a function to a macro?

seancorfield19:09:41

The example is misleading -- it omits the definition for fn in the user namespace.

seancorfield19:09:50

In the first (type fn) the fn in intended to mean "any function in the user namespace"

seancorfield19:09:39

It really could just omit that piece altogether (since it shows type on a function above -- foo).

seancorfield19:09:30

There. Example updated to remove the confusing part. @peter.kehl

seancorfield19:09:50

(I forgot that anyone with a GitHub account could just edit the examples!)

seancorfield19:09:18

In Clojure 1.10, the error message has improved:

user=> (type clojure.core/fn)
Syntax error compiling at (1:1).
Cause: Can't take value of a macro: #'clojure.core/fn

peter.kehl20:09:24

Thanks @seancorfield.

peter.kehl20:09:43

Talking of types: What's a good way to tell if something is a function? Anything that works without namespace import (other than clojure.test/function?), please?

ghadi20:09:26

(don't use fn? -- it is not general)

ghadi20:09:03

btw -- most common is to see class instead of type

peter.kehl21:09:50

Thanks @ghadi. (Unsure how to use class in general, since its result varies: (class inc) returns clojure.core$inc. ifn? is good.

hiredman21:09:32

class returns a java.lang.Class object

hiredman21:09:48

(or nil when passed nil)

noisesmith21:09:39

@peter.kehl what that result shows is that the "function" we call clojure.core/inc is an instance of a class called core$inc in the package clojure - if you checked, you'd find that this class implements IFn

=> (instance? clojure.lang.IFn inc)
true

noisesmith21:09:25

and now that I check for myself, that is precisely what ifn? is doing

pablore21:09:28

So this is how I make test my code with clojure.test:

(deftest some-test
  (testing "function X should do Y"
     (let [arg1 ...
             arg2 ...
             arg3 ...
             expected {...}
             result (my-fun arg1 arg2 arg3}]
        (is (= expected result)))))
Is this a good practice? I find it is easy and simple to test single functions, but more difficult for testing larger functions or functions that mutate state or do IO

seancorfield21:09:38

"more difficult for testing larger functions" -- maybe break those functions up and test the subfunctions instead?

seancorfield21:09:20

(and perhaps test the overall function by mocking those subfunctions via with-redefs)

seancorfield21:09:20

As for mutating state/doing I/O -- one approach there is to separate out the "function" from the "side effect" as much as possible and write tests for the function part. Testing side effecting code is always harder because you need to set up the appropriate "execution environment" before the test (and you should restore the state of the world if possible after the test).

pablore21:09:32

does clojure.test provide with setup and teardown like other languages?

hiredman21:09:35

they are called fixtures

polymeris22:09:25

Does anyone have a working spec for specs? Hopefully covering spec objects, predicates, keywords, regexes and all the other forms that are valid args to e.g. (s/valid? ...)

seancorfield22:09:23

@polymeris It would be a very loose spec since it can be so many things. "predicate" covers anything that can be treated like a function -- including sets, for example.

polymeris23:09:15

s/regex? and s/spec? (for objects) exist

polymeris23:09:38

it think (comp s/spec? s/get-spec) should work for registered spec, but not sure

seancorfield23:09:27

ifn? would cover most function-like things (functions, sets, and also keywords which can behave like predicates!)

seancorfield23:09:30

:foo is a perfectly reasonable predicate. :foo/bar is too -- it doesn't have to be a registered spec... Not quite sure how s/valid? would handle that

seancorfield23:09:34

Ah, if s/valid? is passed a keyword it requires that it is a registered spec!

polymeris23:09:58

Also, have been using orchestra, so decided to check if they had implemented a spec for spec, and as it turns out "TODO: Spec for specs" heheheh

polymeris23:09:27

seems they also came to the conclusion almost anything can be a spec

seancorfield23:09:26

So you'd have to check for keyword? first (and call s/get-spec) and then check ifn? or s/spec? or s/regex? I guess.

alexmiller23:09:30

CLJ-2112 has a work in progress specs for specs

seancorfield23:09:06

Oh, be careful: s/regex? is not a string regex -- it's a spec regex!

seancorfield23:09:36

So you'd need to test for (partial instance? java.util.regex.Pattern) I think?

polymeris23:09:09

yes, I meant a spec regex, like (s/cat ...)

polymeris23:09:28

that's also a valid spec for this use case

polymeris23:09:46

I'll use the not-a-string definition of spec while I wait for CLJ-2112, I guess

seancorfield23:09:26

So it's... (s/or :kw-spec (s/and keyword? s/get-spec) :predicate ifn? :spec s/spec? :spec-regex s/regex? :str-regex (partial instance? java.util.regex.Pattern)) ... anything else needed in there? 🙂

polymeris23:09:49

not that I can think of 🤔

polymeris23:09:29

not sure if str-regexes are valid specs, tho

seancorfield23:09:37

You are correct. Looks like the default case (once s/valid? has checked for keywords and specs is to require something that can be cast to IFn, i.e., ifn?.

polymeris23:09:11

So...

(s/def ::spec (s/or :registered-spec s/get-spec
                    :spec-object s/spec?
                    :spec-regex s/regex?
                    :predicate (s/and ifn? (complement keyword?))))))

polymeris23:09:06

I think I also have to exclude symbols

polymeris23:09:17

(s/valid? ::spec 'x) ;=> true