Fork me on GitHub
#beginners
<
2017-06-02
>
john01:06:11

yeah, same here

john01:06:38

You probably don't need that identity either

john01:06:04

(not (empty? coll)) is a common favorite

john01:06:35

does my-take return nills for n past coll length?

john01:06:41

Ah, no, it doesn't

john01:06:45

You can replace that if with a when and drop that nil.

john01:06:00

one possibility:

(defn my-take [n coll]
  (if-not (zero? n)
    (when (not (empty? coll))
      (cons (first coll) (my-take (dec n) (rest coll))))))

vitruvia01:06:31

It works as intended, just looking for some opinions. I like that idea using when

vitruvia01:06:06

@mudphone thanks its good to know about if-not

john01:06:17

Here's a nother'n:

(defn my-take [n coll]
  (when (and (not (zero? n)) (not (empty? coll)))
    (cons (first coll) (my-take (dec n) (rest coll)))))

john01:06:34

Yeah, your original wasn't bad though.

noisesmith01:06:05

@john that will behave differently for floats, you might want to use zero?

john01:06:26

Aye, corrected above.

noisesmith01:06:57

small typo on that

vitruvia01:06:14

So (not (empty? x)) is better than (identity? x) to check if a sequence is not empty?

noisesmith01:06:26

identity? is true for empty sequences

vitruvia02:06:31

lol I induced you into error, my bad

noisesmith02:06:08

you might notice the doc string for empty? says to use seq instead of (not (empty ...))

noisesmith02:06:14

because seq on an empty thing is nil

matan06:06:52

Are the bindings of let lazy?

matan06:06:13

I have a case where one of my let bindings needs to perform a trace-like side effect on an atom. The side effect does not seem to happen until I use the binding in the expression part of my let form, which turns out unfavorable for my use case.

schmee06:06:14

@matan no:

dev=> (let [a (println "a")])
a
nil

matan06:06:57

@chmee thanks, you are right.

matan06:06:11

my problem must be something else

matan06:06:34

my problem was I assumed doall is transitive, but it turns out if I doall a sequence, but that sequence internally generates another sequence that isn't wrapped with doall, that last sequence's side effects are kept lazy. I am not sure yet of this, as I should probably more deeply learn how laziness and side-effects affect one another.

matan06:06:32

I think one of my sequences was inadvertently kept lazy, hence side-effects on an atom only came about "late".

schmee06:06:04

ahh, tricky 👍

matan06:06:28

annoyingly this does not reproduce in a simplified version of my code, so I'll keep digging.

grierson11:06:40

What is the equivalent of compile time type safety for Clojure? (reasoning: I'm coming from Elm and I really enjoyed the 'if it compiles it works' process.)

grierson11:06:36

I've been using spec (instrument, exercise-fn) which is nice but is there anything else I should be aware of? In other-words what did Clojure developers do before spec to build robust software?

john11:06:03

90% is just the REPL. Though I'm sure Elm has something similar. Schema was (and still probably is) popular. Core.typed attempted to provide a real, actual optional typing system for Clojure/ClojureScript. It's a little verbose though, compared to Elm.

john11:06:10

But any optional type system on CLJ/CLJS is likely to be more verbose than something like Elm.

matan12:06:37

how do you apply (set! print-length 1) to only a single form? I am using that to limit clojure.test's is from printing huge collections when a test for collection equality fails....

matan12:06:48

never used set! before

john12:06:51

try (binding [*print-length* 5] (str [1 2 3 4 5 6 7 8 9]))

matan12:06:10

mmm great 🙂

matan12:06:37

so set! is more global than binding is it...

matan12:06:17

not sure I dig how these two sentences in the docs (https://clojure.org/reference/vars#set) compose: >When the first operand is a symbol, it must resolve to a global var. The value of the var’s current thread binding is set to the value of expr.

john12:06:22

Yeah, binding bindings will only affect things in its own scope.

matan12:06:29

And why does set! have to resolve to a global var? what is the point of set! really?

matan12:06:49

What role does it fill?

john12:06:34

set! will change *print-length* for everything within a given thread. Vars are "thread local."

noisesmith17:06:40

john: only vars declared dynamic are thread local

john17:06:13

Ah, okay.

john17:06:01

how do you do a strikethrough?

noisesmith17:06:31

~foo~ becomes foo

matan12:06:40

Well I guess just a way to override a global

john12:06:56

whereas binding only overrides the meaning of it's bindings within the scope of its form.

john12:06:46

Right, and vars are "global" with respect to a specific thread. Thus "thread bindings."

matan12:06:17

Yeah, I try not to fiddle those things in normal code though.. sticking to functional code whenever possible

john12:06:12

Aye, same here. And binding is more in keeping with that practice than set!. Though I rarely if ever find the need to rebind things either.

vitruvia12:06:36

what is a lazy binding?

john12:06:38

I'm not familiar with a "lazy binding" idiom. Maybe it's a thing? Where are you seeing it?

d-side15:06:38

@vitruvia If you are referring to what's discussed above, it may be a concept where binding an expression to a name does not execute that expression. Instead it's executed when that expression's result is needed somewhere. Following the test given above, (let [a (println "x")]) does print x in Clojure even though a is unused in the body, because Clojure's let-bindings are not lazy. But for a lazy binding nothing would be printed. An example of that would be let in RSpec (Ruby world), let(:a) { puts "x" } would not print anything, unless a was used somewhere in-context. For when it does have to be evaluated immediately, RSpec has let!.

vitruvia16:06:12

yeah you guys were talking about it just now that is why I asked

vitruvia16:06:45

@d-side thanks. I would think the other way makes more sense (a lazy binding would execute the function when called) because that looks more lazy to me than the case where it doesn't execute the function automatically, lol.

d-side16:06:33

It makes sense in a way that "expression? na-ah, will only do when asked about what it returns"

vitruvia16:06:22

that is true lol

vitruvia16:06:30

so the expression is the lazy one, not you xD

d-side16:06:21

I'm lazy too, that's why I tell computers how to do stuff for me. If I get too lazy even for that, maybe it's not worth doing anyway.

d-side16:06:08

But back on topic

dpsutton17:06:02

i'm using boot for the first time. there are a few tasks defined like dev, which modifies the resources available. How can i "execute" this task from the repl after i've jacked in with cider? when i run the task all i get back is a function:

seancorfield17:06:01

dpsutton: (boot dev)

dpsutton17:06:50

hmmm

boot.user> (boot dev)
java.lang.ClassCastException: boot.core$run_tasks$fn__915 cannot be cast to java.lang.CharSequence

dpsutton17:06:07

(deftask dev []
  (uber-env)
  ;; (require '[scratchpad :as sp :refer [dbspec queries]])
  (repl))

dpsutton17:06:24

I'm not sure but i'll look into it later. I appreciate your time. You're always so helpful Sean

seancorfield17:06:38

My bad, it’s (boot (dev))

seancorfield17:06:52

I don’t run tasks very often in the REPL, sorry.

dpsutton17:06:25

ah yeah, i see why that's so weird. i think i'm running a repl inside of a repl haha

seancorfield17:06:50

You’ll grow to love Boot 🙂 We use it for our entire dev/test/deploy pipeline at World Singles. We even build Docker images with it.

dpsutton17:06:57

yeah I need to learn it. I like tasks more than a giant map that I need to figure out how to make it work in lein

dpsutton17:06:06

boot.user> (dev)
#function[clojure.core/comp/fn--4495]

dpsutton17:06:15

(deftask dev []
      (uber-env)
      ;; (require '[scratchpad :as sp :refer [dbspec queries]])
      (repl))

john17:06:28

Correction to what I said above. Vars declared dynamic are "thread local."

lepistane17:06:31

recently at collage we talked about xpath, xquery, xslt those seem ugly to me are those go to tools when working with xml or?

mobileink21:06:05

lepistane: they are hideous indeed, but if you do a lot of xml work, indispensible, and well worth the trouble. xslt is purely functional. clojure has nothing even close to it for munging xml docs, alas.

lepistane07:06:10

i might make something if find myself working with too much xmls thank you

lepistane17:06:48

standard, default how ever u wanna call them

noisesmith17:06:33

we have clojure.xml and clojure.data.xml - I haven’t used them extensively but they make things a little friendlier to use from clojure

noisesmith17:06:53

I have also had good luck with using clj-tagsoup to brute force some data I needed out of a document

john17:06:59

Interesting side note: EDN is a sensible replacement for XML. And with specter-edn (https://github.com/maitria/specter-edn) you can get similar path selector stuff done.

victora20:06:48

Hello! I'm pretty new to functional programming, can you help me understand how would you implement dinamic programming without arrays? 🙂

noisesmith20:06:29

for anything non trivial I would probably use an array actually, this is a viable approach in any clojure environment

noisesmith20:06:00

but for most algorithms we replace mutation of data with recursion, passing a new value for the binding as an arg

noisesmith20:06:37

that is, for any non-trivial problem I solve with dynamic programming, to be clear

noisesmith20:06:14

but honestly it doesn’t come up much (and when it does I’m actually using some library that abstracts it, probably written in java)

noisesmith20:06:28

@victora this talk from the most recent clojure/west has examples of using algorithms that are more natural with mutation, and the process of solving the kind of hard problems where these things come into play https://www.youtube.com/watch?v=TA9DBG8x-ys&amp;list=PLZdCLR02grLp4W4ySd1sHPOsK83gvqBQp&amp;index=13

victora20:06:20

Thanks for the help! Do we have arrays in vanilla clojure? As far as I got into the book I'm reading, we only had vectors and they are immutable. And I'm sure it will not come up in most situations, but I want to learn clojure as best as I can.

victora20:06:26

I'll be sure to check the video

noisesmith20:06:38

@victora clojure is a java library with a built in compiler

noisesmith20:06:45

we have access to everything that comes with the jvm

noisesmith20:06:27

but it’s good to take the time to understand why we leave arrays to special cases, and prefer immutable data, and I’d assume your book would be trying to explain that

noisesmith20:06:16

for a random example, if something goes wrong in my app, I can almost always capture the data in place and store it, and come back to it in a repl and work with it directly

noisesmith20:06:05

without worrying that it got mutated or invalidated later because of other code - I can be reasonably sure that when I use the data later, it behaves exactly like it did in the scope of my function

noisesmith20:06:55

in fact, thanks to a library called transit, I can usually stash the precise data on disk, commit to git, and then use it later in a unit test

victora20:06:54

@noisesmith thank you for the thorough answer

bj21:06:50

I'm trying to write a macro that, in its simplified form, takes an expression and returns a hash-map with the expression as the key and its evaluation as the value, but I'm having trouble unquoting the expression's arguments... For example, given

(let [t "hello world"]
  (my-macro (str t)))
I want the return value to be {(clojure.core/str "hello world") "hello world"} but instead it's {(str t) "hello world"}. This is my implementation so far.
(defmacro my-macro
  [expr]
  `(hash-map '~expr ~expr))

noisesmith21:06:54

don’t you want ~‘expr ?

noisesmith21:06:10

oh, that won’t help

noisesmith21:06:34

` only namespace qualifies literals inside its form, it doesn’t namespace qualify symbols provided from outside

noisesmith21:06:28

and similarly, you can choose to see the form passed in (str t) or the full value, but not some intermediate partial evaluation

bj21:06:17

Hm, alright, thanks. Could I iterate over elements in the form, resolve each one, and reconstruct the form?

noisesmith21:06:32

you can’t resolve locals

noisesmith21:06:55

macros are for transformations that you can do based on the form as it exists in the source file

noisesmith21:06:41

you could emit code that looks things up at runtime, but that doesn’t work so nicely with locals, and doing that is fighting against the grain of clojure

bj21:06:06

Sounds good. The initial goal was to memoize certain functions for the duration of an http request. This was obviously a simplified form of the macro. I decided to use this approach as I could have the whole map GC'd after I'm done with the request, and keys were straightforward. Anyway, I'll probably use an approach that just uses memoize and no macros