Fork me on GitHub
#clojure
<
2016-11-17
>
lprefontaine00:11:45

Sean, I am fine with the filtered obscenities in the bad word list, there's none in french 😜 I have a huge repertoire handy 😂

PB02:11:13

Is it possible to use extend-protocol for a call with 0 args?

Niclas09:11:55

I'm quite interested in moving all my tests from separate test files into the metadata of functions, from:

;; my-ns.clj
(defn my-fn
  [x]
  (identity x))

;; my-ns-test.clj
(deftest test-my-ns
  (is= (my-fn "abc") "abc"))
... to:
;; my-ns.clj
(defn my-fn
  {:test #(do
            (is= (my-fn "abc") "abc"))}
  [x]
  (identity x))
I can see many benefits from this and only one real drawback - making the code more cluttered. Has anybody had any experience in structuring tests like this and what worked well/not so well?

yonatanel09:11:15

@looveh Take a look at clojure.spec. I wouldn't put tests with example data trying to cover all cases right there in the function. http://clojure.org/guides/spec#_spec_ing_functions

Niclas09:11:59

I’m using spec and generative test for edge cases and conformity to complement my unit tests that verify actual business logic. My proposal here was to move the separate unit tests into function metadata

yonatanel10:11:23

@looveh Aren't you happy with tests being on a separate file? How many tests per function do you have?

Niclas10:11:14

@yonatanel It works, but I’m always looking for ways to improve! About 1-10 tests per fn I’d say

PB13:11:28

I’m not sure how many of you are using datomic. But I recently “finished” my first itereration of a library to help with the querying of datomic entities. Any feedback would be greatly appreciated: https://github.com/petergarbers/molecule

Alex Miller (Clojure team)13:11:33

There is #datomic too btw

spacepluk15:11:18

hi, any idea how I can put a cross-plaform try/catch inside a reader conditional?

spacepluk15:11:17

that doesn't work, I tried (def err-sym #?(:clj Throwable :cljs Error)) and then using err-sym in the macro but it didn't work either

naomarik15:11:47

what're some useful ways to explore large datastructures? like importing a massive xml file with loads of nodes

moxaj15:11:16

@spacepluk macros are clojure macros (unless you are using self hosted cljs), so reader conditionals within the macro will always take the :clj path

moxaj15:11:42

however, you can check (boolean (:ns &env)) at compile time - if it is true, you are in cljs env

gfredericks15:11:24

a lot of times the variation can be pushed down into a function

PB15:11:07

@seancorfield Has this been an issue?

spacepluk15:11:44

@moxaj that makes sense. there's no workaround, right?

moxaj15:11:02

@spacepluk well, this is the workaround 🙂

spacepluk15:11:06

ah, sorry I missed your second line hehe

spacepluk15:11:18

thanks! 🙂

seancorfield15:11:59

@petr are you referring to my reminder about the CoC?

seancorfield15:11:59

We've had a lot of new folks join recently and one of them was using profanity that isn't appropriate in a professional environment so I thought it was worth a reminder about the CoC. Mostly this community is pretty well-behaved (unlike a few others that I admin, also with a CoC in place).

dpsutton15:11:23

i use profanity in my office 😞

seancorfield15:11:49

Further discussion related to conduct or the CoC should probably go to #community-development so we can keep this channel technical and professional simple_smile

hwk15:11:50

this is a weird question -- is there a way to pass clojure.spec specs as an argument ?

hwk15:11:12

can I write a function of the form (fn [z y]) where x resolves to a symbol ::foo and (s/asserts ::foo y) holds ?

hwk15:11:41

yeah, just tested it in a repl

Alex Miller (Clojure team)15:11:41

user=> (defn foo [s v] (s/assert s v))
#'user/foo
user=> (s/check-asserts true)
true
user=> (s/def ::foo int?)
:user/foo
user=> (foo ::foo "a")
ExceptionInfo Spec assertion failed
val: "a" fails predicate: int?

hwk15:11:49

this is amazing -- being able to pass a spec as an argument

hwk15:11:07

it seems completely counter intuitive when viewed as the perspective of types -- but when viewed as specs, it makes perfect sense

roelofw15:11:31

which testing framework can I use the best ? When I google I see some alternatives ?

Alex Miller (Clojure team)16:11:03

clojure.test is built in for example testing and is the most common. expectations takes a different approach to unit testing and does great error reporting. test.check is the best choice for generative testing.

roelofw16:11:47

oke, then I will look some tutorials about clojure.test

Alex Miller (Clojure team)16:11:47

Clojure Applied (I am a co-author) has a chapter that covers all of the above

roelofw16:11:22

@alexmiller Looks a very interresed book where I can learn from every chapter.

roelofw16:11:38

Is every chapter a own programm ?

hwk16:11:53

(def ^:dynamic state) is there a short hand for: (set! state (assoc state ...)) ?

seancorfield17:11:33

@roelofw If you like the look of Expectations after reading that chapter, there’s an #expectations channel here and I’m now one of the lead maintainers (planning to add support for clojure.test tooling for Expectations).

seancorfield17:11:38

@hwk Perhaps consider an atom and then you can use swap! — what are you using global dynamic state for in Clojure?

hwk17:11:20

@seancorfield: I'm writing a forth interpreter; there is a state consisting of :data-stack, :env, :code-stack

hwk17:11:37

@seancorfield : I would prefer to not pass the arg around all the time, and thus a dynamic var

spacepluk17:11:33

how do I reference js/Error at compile time? 😓

roelofw17:11:57

@seancorfield I think I will read all chapters

seancorfield18:11:19

@hwk Sounds like you should still use an atom for your (global) state rather than just modifying a Var...

seancorfield18:11:37

(defonce state (atom {}))

seancorfield18:11:43

Then you can use (:data-stack @state) etc to read parts of it and (swap! state update-in [:data-stack] conj new-item) etc to update it safely.

hwk18:11:21

@seancorfield : true, but with dyanmic vars, I can have multiple threads, each running an interpreter, (as they use different dynamic bindings); with a global, I'm forced to only having one interpreter

Emma18:11:10

Hi. Could you please explain how this lazy evaluation works. (first (map println (range 10))) prints the whole sequence(when I expected it to print only 0 and then return nil). I don't understand mechanics behind this behavior. Thanks

hwk18:11:06

mik: that's an awesome question!

hwk18:11:14

try this: (first (map println (range 999))

hwk18:11:25

on mine, it only prints up to 31 @mik

hwk18:11:41

I think what's happening is this: on "first", it's executing the lazy map in chunks? ...

Emma18:11:18

@hwk, ah yes, I completely forgot 'bout chunked evaluation. Yes it gives up to 31 elements.

Alex Miller (Clojure team)18:11:50

Don't mix side effects and lazy seqs

hwk18:11:39

@alexmiller: can the 'mapped function' get exec-ed more than once, or is the result 'cached' aftertwards? i.e. in (first (map println (range 999))) is there a way to force (println 3) to exec more than once ?

hwk18:11:55

I heard that atom functions can be exec-ed more than once, but is it also true of mapped functions?

Alex Miller (Clojure team)18:11:02

Seqs cache their values

Alex Miller (Clojure team)18:11:37

So the function will be executed once per each element in the input seq

Alex Miller (Clojure team)18:11:40

Most of the "do" functions are designed for side effects. Also look at run!

Emma18:11:09

@alexmiller it is mostly about understanding how stuff works. I use "do" family of functions for things that require side effects.

hwk18:11:43

@mik: ask more questions -- I didn't realize map did chunked evaluation until I tried to figure out wtf was going on with your example

hiredman18:11:44

map preserves chunkiness in the source sequence

hiredman18:11:11

so a sequence result from map may or may not be chunked depending on the input

hwk19:11:09

@hireman: (first (map (println (list 1 2 3 4 5)))) -- damn ... how do you pick up knowledge like this (besides reading the clojure source) ?

hiredman19:11:44

chunked sequences are my go to worst feature, so I know some things about it

Alex Miller (Clojure team)19:11:04

Chunked seqs are a big reason why lazy seqs are fast

Alex Miller (Clojure team)19:11:58

IMO if you ever care about how much of a lazy seq is realized and when, you should prob not be using lazy seqs

bbloom19:11:57

@hwk I use this macro from time-to-time: (defmacro update! [v f & args] `(set! v (f v @args)))

bbloom19:11:15

it needs a better name, since it’s confusing with update/update-in, but it is basically swap! for a dynamic variable

bbloom19:11:35

another common approach is just to do (def ^:dynamic foo (atom 0)) and then you can just use swap! directly

bbloom19:11:02

i’ll say that interpreters and porting traditionally imperative algorithms are the one time i actually feel a need for this

bbloom19:11:34

when there’s no side effects, it’s almost always preferable to use a function

bbloom19:11:43

and then use either run! or reduce

fellshard19:11:22

oh gosh twitter-sized part-golf'd clojure

fellshard19:11:26

Oh it's a tiny stack lang?

bbloom19:11:42

indeed - since hwk said he was experimenting with a stack interpreter

hwk19:11:11

@bbloom: yeah, I was initially trying to do it in a pure manner, but then I realized I had a bunch of code of the form (let [ [state0 a] ... [state1 b] ... [state2 c] ... ] ... then it was "oh, this is clearly a Either + State Monad, for storing the state + catching any errors" then it was "well, clojure algo monads doesn't work all that nice without types, .... so maybe I can do it with try/catch instead of Either, and with some set! instead of state monad" ... and now this is where we're at :-)

bbloom20:11:32

@hwk what are the a, b, c? can they be stashed in the state itself? composition loves singular data objects

bbloom20:11:50

a forth interpreter is a naturally imperative thing, so you can set “flags” in the state

bbloom20:11:04

(let [state] (assoc state :a-flag a) …

hwk20:11:24

@bbloom suppose we were implemetngint eh + function, in haskell, it would be like a <- pop_data b <- pop_data push_data (a+b)

bbloom20:11:27

then you can replace your let with (-> state step-1 step-2 step-3)

hwk20:11:32

in coojure, it becomes a mess of let with state0, state1, state2

hwk20:11:39

with (->) ... we "can't capture" the values

bbloom20:11:41

ah, yes, you want to capture intermediate values

hwk20:11:46

yeah 😞

bbloom20:11:08

have you tried state = {:values {:a 1 :b 2 …}}?

hwk20:11:13

@bbloom: btw, it's very nice talking to you ; clearly you suffered through these problems yourself

bbloom20:11:23

it’s a dynamic version of what haskell does

hwk20:11:37

I don't know how you can do that with state, state has the form {:data_stack ..., :code_stack ..., :env ...}

bbloom20:11:01

so that’s the state of your program being interpreted, but you can have “meta state"

dpsutton20:11:01

can you make it more like (-> [state] step-1 step-2) and have steps return the vector of state, pushing and popping?

bbloom20:11:22

{:data …, :code …, :env …, :whatever …}

dpsutton20:11:35

yeah, making your data structure of state a bit more rich?

bbloom20:11:49

you can also create a scoping construct for your meta-level binders

hwk20:11:38

I see, so it's like ::machine-state = [::data-stack ::code-stack ::env], ::state =[::machine-state ::aux-state]

hwk20:11:46

where :aux-state stores stuff like a = ..., b = ...

hwk20:11:51

never thought this way, very interesting

dpsutton20:11:32

if you make :aux-state a vector of maps, you can then have scoping and scope holes

dpsutton20:11:45

i think my metacircular scheme interpreter from school worked this way

bbloom20:11:51

(defn bind [state k v] (assoc-in state [:bindings k] v)
(defn scope [{old :bindings, :as state} f] (-> state f (assoc :bindings old)))

hwk20:11:21

@dpsutton: I've written a metacircular scheme interpreter before. I don't know what "scope holes" are.

dpsutton20:11:13

that a is bound twice. but it should resolve to 2

bbloom20:11:35

admittedly, this is a shortcoming of writing interpreters in clojure: there’s no static way to interleave interpreter state and machine state

hwk20:11:35

@bblom: This is really cool. Your method has this additional benefit that when I dump the state, there's a "more informative" debug output.

dpsutton20:11:38

so if you had a stack of maps with bindings, you just start at the top of the stack and search for your first binding of a

hwk20:11:17

@dpsutton: right, like a flattened view of each env pointing at it's "parent"

dpsutton20:11:31

and then you can just pop them off

bbloom20:11:47

yeah, so the scope function i provided uses the clojure call stack

bbloom20:11:56

you could also do it more explicitly with push/pop operations

bbloom20:11:24

then scope would be (-> state (push-bindings old) f pop-bindings)

bbloom20:11:10

btw, writing traditional interpreters in clojure kinda sucks due to lack of tail calls - but if you fully reify the machine state, then clojure is awesome for interpreters, if a bit slow

hwk20:11:52

this is for educational purposes, not real use, slow is fine

hwk20:11:07

there is something nice about writing + as (-> (state) (pop) (pop) (apply +) (push))

hwk20:11:23

writing a forth interpreter function w/o using variables is devious in some way

bbloom20:11:57

so if you read some of chuck moore’s writings, he laments the popularity of lexical variable schemes in various forth implementations

bbloom20:11:15

this video gave me some very deep insights: (defn scope [{old :bindings, :as state} f] (-> state f (assoc :bindings old)))

bbloom20:11:59

the interesting bit is just how much use of dynamic variables there is

bbloom20:11:07

and how it works quite nicely for imperative stuff

bbloom20:11:59

which is one of the reasons i have that update! macro 😉 i find it much easier to reason about imperative algorithms written in a forth-ish style, albiet without the clutter of stack shuffles etc

hwk20:11:13

oh man, this is brilliant, I'm going to use (pop-as :a :b) (push-apply [f :a :b]) // for implementing +-*/

hwk20:11:29

then dup can be (pop-as :a) (push :a :a)

dpsutton20:11:48

i can't wait to watch that video later

dpsutton20:11:53

i'm at work now. thanks for sharing

josh.freckleton20:11:09

can extend-protocol dispatch based on the second position, as in (map f <dispatch-on-my-type>)

bbloom20:11:58

@josh.freckleton no, use another function to shuffle the arguments

josh.freckleton20:11:07

@bbloom ah, perfect, thank you!

hwk20:11:38

(s/assert ... args) ;; how do I use spec, in ..., to say "args is a list of keywords" ?

hwk21:11:08

(s/* keyword?)

Alex Miller (Clojure team)21:11:25

either that or (s/coll-of keyword?)

Alex Miller (Clojure team)21:11:23

they aren’t much different if you are using those independent of any other spec

Alex Miller (Clojure team)21:11:38

but it will matter if you include that into another regex spec or not

Alex Miller (Clojure team)21:11:54

generally, I always use a regex op (s/* here) for arg specs

fellshard21:11:06

Point-free interpreter? 🙂

hwk21:11:51

point-free interpreter implemented in point-free clojure

fellshard21:11:16

Hmm. I hadn't thought of stack-based langs that way, but I guess you could consider it point-free...

nikki22:11:44

@bbloom i did something like this with first-class envs and dynamic binding in JS: https://gist.github.com/anonymous/d6476b301685c6cb0ce7b523496bf9c1

nikki22:11:29

as you can see you can do Self-like prototype inherited objects cuz the scopes are reified in this way

hwk23:11:51

given x and [f, g, h, i, j], is there a builtin for (j (i ( h ( g ( f )))))

hwk23:11:16

(reduce #(%2 %1) x [f g h i j])

fellshard23:11:09

comp works from the outside in, so you'd need...

hwk23:11:26

@tom, @fellshard : oh right, comp + reverse, thanks!

fellshard23:11:27

Which isn't much cleaner than the reduce option in the end...

hwk23:11:37

in that other language, it could be written as (comp . reverse)

fellshard23:11:38

But maybe makes it more reusable

fellshard23:11:51

we do not speak of that language 😛

hwk23:11:53

All types are created equal. Functions have no right to dictate what type of data they will and will not process.