Fork me on GitHub
#clojure
<
2017-07-25
>
hmaurer11:07:49

I have seen it. Mind-blowingly awesome. It’s on my todo-list to implement something similar 🙂

minikomi10:07:52

does anyone have any idea how to encode the ::selection selector in garden ?

minikomi10:07:08

ah.. need to define a pseudoselector.

hmaurer14:07:12

Is it a common/recommended practice to add tests as metadata directly on functions?

misha14:07:09

I can't apply macro, can I?

misha14:07:25

I essentially want to

(let [specs [:a :b]]
  (apply clojure.spec.alpha/cat (interleave specs specs)))
what my options are?

misha14:07:02

@U2PGHFU5U thanks, I just forgot about ~@ splicing. it's all good now

dominicm14:07:58

write a macro to do it 😞

mpenet14:07:19

pick your poison 🙂

misha14:07:14

oh, wait, there is a ~@

mpenet14:07:27

(let [specs [:a :b]]  (eval `(clojure.spec.alpha/cat ~@(interleave specs specs))))

misha14:07:28

forgot about it

misha14:07:39

exactly, thanks

misha14:07:56

wait, why eval?

misha14:07:12

ah, it's not wrapped in macro.

mpenet14:07:50

yep, I prefer eval personally for that stuff. a macro def will stay here in all its uselessness after you used it to generate your spec

mpenet14:07:04

depends if you need to do that a lot or not

misha14:07:53

I wanted a macro initially, because I have too many s/cats where I basically reuse spec names as dispatch keys

misha14:07:08

figured I'd try to just re-use spec names instead of coming up with throw-away names time and time again.

misha14:07:45

while we are on macro subject: how do grown ups validate macro's input (e.g. with spec)?

dominicm15:07:36

I use spec lately. asserts are great too though.

Alex Miller (Clojure team)15:07:01

will give you the destructured version. if it’s ::s/invalid, then s/explain.

misha16:07:51

is there a way to get fn's arity? like

(arity filter)
;; => [1 2]
actually I am not even sure why I am asking this dafuq

delaguardo17:07:06

If function was defined with defn - (:arglists (meta #'filter))

henrik17:07:27

@misha In cljs, you can do (.-length (.-constructor (.-prototype render)))

henrik17:07:52

Where render would be the function, for example

henrik17:07:04

Or why not (-> render .-prototype .-constructor .-length). We’re not barbarians after all.

qqq17:07:21

anyone else find code written via reduce to be "obfuscated" ?

qqq17:07:49

for some reason, when writing code involving reduce, it's always: 1. how can I write this imperatively ? 2. then I reformulate it as reduce

qqq17:07:39

@henrik: no concrete example, just that most of the time, I want to do:

(let [state (atom ...)]
  (doseq ...))

qqq17:07:57

then I end up "inverting" the doseq / modification to the @state atom in order to get my reduce code

henrik17:07:30

@qqq There’s nothing inherently wrong with that, in my opinion. For me, it’s a quite common pattern that I pull out the transducers once I start noticing boilerplate and repeating patterns in the code.

qqq17:07:30

@henrik: have you used Haskell? the haskell solution to this would be the 'state' monad, then an external sequence_

henrik17:07:23

@qqq Unfortunately I haven’t used it beyond the tutorial on the home page (which is quite nice)

henrik17:07:54

map does seem to be easier to visualize than reduce though. I think map corresponds more to everyday patterns of life. You can imagine walking along a row of potted plants and watering each for example, getting a row of watered plants.

henrik17:07:33

But what would be the reduce version of that? Repot them in one large pot, one plant at a time?

henrik17:07:43

But also, the order in which they were repotted would somehow matter. I don’t know, it’s not as straightforward.

chrisbroome17:07:46

@qqq in Haskell how would you solve the problem?

chrisbroome17:07:59

foldr over a sequence?

chrisbroome17:07:27

(I'm just curious)

qqq17:07:36

@chrisbroome : I would write the code "imperatively" with putState / getState, then put it on a do block + use sequence_ if there's a list

bfabry17:07:14

I find it easier to go write using loop/recur -> refactor to reduce

qqq17:07:30

the point is that the code goes something like:

s <- getState
... do some processing ...
putState s'
but then the reduce somehow causes me to "invert" the state manipulation

bfabry17:07:42

generally, than doseq. because that way you just delete a bunch of code and change some keywords. refactoring doseq and atom updates is annoying

qqq17:07:17

@bfabry : loop/recur is definitely closer to reduce than doseq/atom

qqq17:07:48

nevertheless, it seems that in an otherwise clean language, there's this mismatch between "mental thought" and "how code has to be written"

qqq17:07:09

or maybe I just haven't mastered reduce idioms

bfabry17:07:02

mmmmmm my assumption is that that mismatch is just a result of spending so much time in imperative languages. I definitely find myself having to go through that interim step a lot less than I did 4 years ago when I moved ruby->clojure. could be wrong though

qqq17:07:01

haskell is not exactly '

qqq17:07:08

imperative' -- some would say it's even more 'functional' 🙂

henrik17:07:29

@qqq Is there no notion of reduce in Haskell though?

noisesmith17:07:39

that’s foldl

bfabry17:07:43

there is, and it would be considered way more idiomatic than using the state monad

qqq17:07:46

haskell definitely has foldl / foldr, but it also has state monads + sequence_

noisesmith17:07:10

and it’s provable that everything you can do via state manipulation you can do in a left fold / reduce, it’s just a different code pattern to do it

qqq17:07:19

I would say, any time you're bashing state, state monad is more idiomatic than folds

qqq17:07:30

and with reduce, the initial value is basically your 'local state'

bfabry17:07:33

the state monad is a "purely functional statically typed" math trick around writing an imperative looking thing and still keeping it pure

qqq17:07:07

yeah, I guess 99% of the problems I have with clojure is: it doesn't have haskell's type system 🙂

bfabry17:07:27

funny, that's my favourite thing about clojure 😆

noisesmith17:07:43

@qqq frege is haskell for the jvm, and it would even be usable except it is too hard to use interfaces or extend java types (because it really does implement haskell’s type system - which is better but also alien to the platform)

qqq17:07:24

I tried using frege once; I found neither the documentation nor the community helpful.

noisesmith17:07:29

I would have totally used it for some things in my codebase but not being able to define or implement interfaces (not to mention concrete inheritence, which clojure taught me how to do without), it was a no go

tomjkidd17:07:32

Is there a way that others are using tools like eastwood with files that contain namespaced keywords?

kjothen18:07:35

I don't recall any problems with namespaced keywords, but I seem to remember I had problems with namespaced maps. As these were limited to my test classes, I added these namespaces to the ignore list I think. Also, I think there were some fixes on the master branch that remain unreleased. A bit vague I realise, but if you're still stuck tomorrow (unlikely) then I'll check when I'm back in front of a computer...

tomjkidd18:07:39

Ah, to be more specific, I am using lein eastwood '{:add-linters [:unused-namespaces] :namespaces [:source-paths] :exclude-linters [:unlimited-use]}' as my command, and I get an error for a namespaced spec (so you might be right that a namespaced keyword would not create problems, sorry!).

tomjkidd18:07:04

== Eastwood 0.2.4 Clojure 1.9.0-alpha17 JVM 1.8.0_121
Exception in thread "main" clojure.lang.ExceptionInfo: Invalid token: ::spec-ns/spec-name {:type :reader-exception}

tomjkidd18:07:21

(ns acme.spec-ns
  (:require [clojure.spec.alpha :as s]))

(s/def ::spec-name string?)

tomjkidd18:07:46

When I use (:require [acme.spec-ns :as spec-ns]) ... (clojure.spec.alpha/validate ::spec-ns/spec-name) ...), this is the context I have using a namespaced spec in

noisesmith17:07:51

@qqq I think their assumption is “just do it the way you would in haskell”

bfabry17:07:53

thing is clojure just has no need for a state monad, because if imperative code is what's appropriate there are mutable options

henrik17:07:57

Well, cognitively, our problem solving is very pattern based. A problem occurs->go fetch the nearest solution from memory that approximately fits problem->apply to problem. Certain mismatch is bound to happen when encountering a somewhat different paradigm.

henrik17:07:27

Personally, I find macros to be some of the most intractable marshlands of the language. The parser in my head doesn’t predict the output of a macro well at all.

seancorfield17:07:12

@noisesmith I really liked the promise of Frege and spent quite a bit of time working with it -- I wrote a Leiningen plugin for it and put out some mixed Clojure / Frege code examples. But, ultimately, I just found it too frustrating to use and kept going back to Clojure... which kinda fits in with the love/hate relationship I've had with Haskell ever since it appeared (I always hoped Haskell would rule the world but it seemed like the team behind it tried very hard to make sure that didn't happen!).

noisesmith17:07:59

yeah I think I might have first heard of frege from you - and if I could have just defined / implemented interfaces I could have used it for my project…

seancorfield17:07:35

I just watched Prof Turner's talk about "Some History of Functional Programming Languages" from the Poland FP conference this year. Great to hear how everything came together (he was going to be my external examiner for my PhD thesis back in '86... if I'd actually completed it!).

seancorfield17:07:53

We used Miranda at my university and I was familiar with ML and SASL and a bunch of the other FP languages that all merged into Haskell. I created my own (SURE), just like all the other FP researchers in England at the time! 🙂

hmaurer18:07:10

Whereabout did you study?

noisesmith18:07:51

I remember checking out CLEAN shortly after I got into software (by reading “tech yourself C++ in 21 days”)

noisesmith18:07:10

in retrospect I’m amazed I got turned to such an interesting direction so early on

seancorfield18:07:59

University of Surrey (Guildford).

seancorfield18:07:19

SURE was Surrey University Recursive Evaluator 🙂

hmaurer18:07:25

ooh, makes sense!

hmaurer18:07:38

Hi! I have the following in a Datalog query:

'[:find ... :where [(wef-backend.app.identity/past-expiry? ?expiry)]]]

hmaurer18:07:01

Due to how Datomic/Datalog works I must fully qualify the past-expiry? function, even though it is available in the namespace where I wrote this query

hmaurer18:07:09

is there some clever trick that would let me omit the full qualifier?

hmaurer18:07:27

I tried syntax quoting on the query but that doesn’t work, as it fully qualifies the ?expiry, etc variables too

hmaurer18:07:07

Essentially I would like to fully qualify some symbols in that quoted expression

hmaurer18:07:11

but not others

noisesmith18:07:08

@hmaurer you can use ` for this, and selectively use ~ for the symbols you want to namespace qualify

hmaurer18:07:37

@noisesmith how so? I tried variations on your suggestion but didn’t manage to get it to work

noisesmith18:07:56

peregrine.circle=> `[:find ... :where [identity ~'?foo]]
[:find ... :where [clojure.core/identity ?foo]]

noisesmith18:07:11

I just picked a random thing with identity in the name, but I hope it translates

noisesmith18:07:26

I guess I should have explicitly mentioned the ~' idiom to prevent namespacing

hmaurer18:07:36

is there a way to do the opposite? selectively pick the symbols I want to namespace qualify

noisesmith18:07:43

that’s harder

hmaurer18:07:46

instead of the ones I do not want to namespace qualify

hmaurer18:07:51

nevermind then

hmaurer18:07:53

thanks 🙂

noisesmith18:07:37

this is close, but no cigar

peregrine.circle=> '[:find ... :where [`identity ?foo]]
[:find ... :where [(quote clojure.core/identity) ?foo]]

hmaurer18:07:51

yep, not quite 😕

hmaurer18:07:14

why wouldn’t this work:

'[:find ... :where [~`identity ?foo]]
?

noisesmith18:07:34

~ doesn’t make sense outside `

hmaurer18:07:52

why does it make sense here:

`[:find ... :where [identity ~'?foo]]
?

noisesmith18:07:10

it’s inside the ` at the beginning of the vector

hmaurer18:07:55

right, but why does swapping out the use of syntax quoting vs normal quoting makes it different in use?

hmaurer18:07:01

(I’m not familiar enough with quoting yet)

noisesmith18:07:08

because ~ is only valid in syntax quote

noisesmith18:07:35

it’s a special operation that is only defined in that context

hmaurer18:07:35

using it outside throws

java.lang.IllegalStateException: Attempting to call unbound fn: #'clojure.core/unquote

hmaurer18:07:41

does this only get bound within a syntax quote?

hmaurer18:07:50

or is that a different error?

noisesmith18:07:54

right, it exapands to unquote, that is undefined outside `

noisesmith18:07:14

this is all stuff implemented in the java code of the reader

noisesmith18:07:25

it isn’t built on the normal machinery, it’s the basis for it

hmaurer18:07:42

is unquote a special form?

noisesmith18:07:59

syntax-quote ` is special, and it handles unquote

hmaurer18:07:18

so unquote will get evaluate somehow within a syntax quote

hmaurer18:07:23

but outside of it it has no meaning

hmaurer18:07:27

and even inside, it’s not a function

hmaurer18:07:32

it’s just a symbol that ` interprets

noisesmith18:07:37

right, it’s a parser machinery basically

noisesmith18:07:04

we’re at the point in this convo where I’d probably have to read more code to answer more questions though

hmaurer18:07:23

Luckily for you I don’t have more questions at the moment 😄

noisesmith18:07:34

like whether there’s an actual unquote method somewhere in Compiler.java or if it’s entirely a figment of syntax-quote

hmaurer18:07:13

For now I think I am contempt with the answer that clojure.core/unquote is not a function, but something that has a special interpretation within a syntax quote

noisesmith18:07:44

there’s this, but I am not totally sure what it means - UNQUOTE used to be defined as a special symbol but now it is commented out https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L70

bronsa18:07:41

unquote never makes it to the compiler

bronsa18:07:06

it's handled by the reader earlier than that

hmaurer18:07:39

@bronsa could you point at where exactly it is handled?

bronsa18:07:26

from within the implementation of syntax quote

bronsa18:07:08

the tl;dr is that when the compiler sees ~foo it expands into (clojure.core/unquote foo)

hmaurer18:07:23

@bronsa ah, so if it’s an unquote form it simply gets rid of the “unquote” and “quote” bits at reading time?

bronsa18:07:38

then syntax-quote walks its expression and expands all the unquoted forms

bronsa18:07:22

you can see how that gets transformed by quoting a syntax-quoted expression

bronsa18:07:34

user=> '`(~a)
(clojure.core/seq (clojure.core/concat (clojure.core/list a)))

hmaurer18:07:38

so the reader would transform (clojure.core/unquote 'foo) into foo?

bronsa18:07:46

it's more complex than that

bronsa18:07:49

because of splicing

hmaurer18:07:08

if there was no splicing then it would be this simple?

bronsa18:07:16

more or less, yes

bronsa18:07:40

but because of splicing, it has to wrap every subform that is unquoted in a list & concat them all

bronsa18:07:49

so splicing becomes just like unquote w/o the wrapping list

bronsa18:07:57

user=> '`(~@a)
(clojure.core/seq (clojure.core/concat a))
user=> 

bronsa18:07:16

because the list is already in a (so to speak)

bronsa18:07:51

the impl is quite more complex because it has to deal with maps/vectors/sets other than just lists

bronsa18:07:54

but that's the basic idea

hmaurer18:07:11

thanks! that clarifies things

martinklepsch18:07:26

has anyone written about how to organize specs when they don’t perfectly map to namespaces etc?

noisesmith18:07:37

I’d be interested to see a well reasoned pitch justifying the practice

noisesmith18:07:48

your data domain doesn’t match your namespace organization?

tjtolton19:07:04

organizational/insitutional inertia?

tjtolton19:07:48

because that's why my namespaces don't match my data domain.

tjtolton19:07:07

Professional software dev is hard, man. Companies are hard.

hiredman19:07:58

it seems like people are really tied to namespaces on keywords matching the namespaces code is organized in to

hiredman19:07:06

there is no reason for that

hiredman19:07:51

code should change and reorganize way more often than your data formats

hiredman19:07:33

The only time I ever use a code namespace name on a keyword is when I am sure it is temporary and will change

martinklepsch19:07:45

well lets say I have some type of domain object. Most functions operating on that are in some namespace that might contain multiple spec’d domain objects. To define specs for these objects I’d have to do something like (s/def ::my-object-name string?)

martinklepsch19:07:51

does that make sense?

hiredman19:07:02

never use :: and instead of tying to the namespace the code is currently in, use a namespace like :my-org.project/whatever

martinklepsch19:07:20

ok yeah, that seems like a sensible argument

kurt-o-sys19:07:22

right... I'm also more and more inclined to not use ::, but using data domain logic.

hiredman19:07:34

your specs are like a database schema, imagine if in tables you named columns after the code that is currently accessing it, terrible

hmaurer19:07:09

Yeah, I am not sure why they use :: everywhere in spec’s doc

hiredman19:07:33

it makes for shorter docs

wilkerlucio19:07:22

@hiredman another option is to have namespaces dedicated to your specs, I've being doing that and I quite like it, you can have a namespace like your-org.specs and inside of that you create the namespaces with your data definitions, by doing that the :: gets very convenient, also you ensure very long namespaces so you have a more strong uniqueness sense

kurt-o-sys19:07:03

Yeah, but putting all specs in there isn't much fun either...

kurt-o-sys19:07:35

I've been splitting the specs and where to put them and how to name them depending on the purpose.

qqq19:07:15

here's something easy to do with state monad but non-obvious to me how to do with reduce: I have a expression tree. I want to compile it to Three-Address-Code, i.e. a list of statements where each statement is (set! a (op b c)).

bfabry21:07:24

how would you do it with the state monad? I think the trickyness here is more traversing trees than building up state

bfabry21:07:15

I would probably just use an atom and postwalk, fwiw

bfabry21:07:12

oh... or tree-seq. which I found out about just now

qqq00:07:52

imho, the trick part is assigning a unique var to every node of the tree

qqq00:07:06

with a state monad, you just keep an int counter, and increment it as you traverse the nodes of the tree

val_waeselynck19:07:07

I'm writing a blog post on REPLs, can anyone recommend a short video demonstrating the Clojure REPL?

hmaurer20:07:13

Out of curiosity, what exactly will be the focus of your blog post?

val_waeselynck06:07:03

@hmaurer 'what makes a good REPL?' - what value do REPLs bring, what features make for good REPL, what programming language features enable them

val_waeselynck06:07:53

@U064X3EF3 a bit too long :) I need a 2-minutes thing

plins20:07:26

is anyone aware of an async ring (compojure) middleware to log requests and responses?

hiredman20:07:14

@qqq clojure's reduce isn't really for folding over trees. it only folds over seqs. so you'll need to write your own fold over a tree or flatten the tree first

hiredman20:07:09

https://gist.github.com/hiredman/2b2707d74ad2ff0b2f941b965f7dd97f#file-ref-clj-L3-L13 is an example of a custom reduce implementation over a tree using cps

hiredman20:07:23

I don't know what your expression tree is like, but if you can transform it in to something like anf generating three address code will be much easier

heucles20:07:14

Hi all, I´m just starting with clojure, currently I am reading "The joy of clojure", and while trying to implement an example from the book, I am getting the following error on REPL:

user=> (def frame (java.awt.Frame.))

CompilerException java.lang.reflect.InvocationTargetException, compiling:(form-init6212165196188451204.clj:1:1) 

user=> frame
#object[clojure.lang.Var$Unbound 0x4aabdabc "Unbound: #'user/frame"]
I was wondering if someone could point me in the right direction?

hiredman20:07:13

*e will show you the whole stacktrace

noisesmith20:07:31

also, the most likely cause of that error for that code is using a headless JVM

noisesmith20:07:37

which won’t include awt

noisesmith20:07:01

@heucles what does (resolve 'java.awt.Frame) say?

heucles20:07:27

hey @noisesmith I think you got it right, I´m in another environment right now. But I recall a message mentioning something about "Headless", if that is the case, do you know how can I solve it?

noisesmith20:07:02

by installing a jvm that’s meant for doing desktop /gui stuff

noisesmith20:07:21

but that shouldn’t be urgent for learning clojure (though playing with windows and widgets can be nifty)

noisesmith20:07:12

typically on eg. a debian system, you can pick different jvms and decide which is default, and the one they pick for you if you aren’t too bothered to customize doesn’t come with the desktop and graphics stuff just to keep default installs smaller

noisesmith20:07:40

but the process of getting the right jvm installed for the features you want will vary based on your OS

heucles20:07:42

@noisesmith I thing I´ve got it, I´ll try to fix it later and I´ll let you know, but anyway I will follow your advice and wont let it get to much in the way of my progress, thank you very much!

Kevin Lynagh22:07:58

I'm trying to wrap my head around core.async/pipeline-async, and it seems to be running more parallel tasks than I'm requesting: https://gist.github.com/lynaghk/01aaa39bedcd2d37032dba3b68d9f94f

Kevin Lynagh22:07:51

Is the "parallelism n" in the docstring more of a suggestion than a hard limit? Even when setting n=1, I'm getting three async tasks running at the same time.

hiredman23:07:13

pipeline-async is really weird, the other two pipeline variants are much easier to work with and much more likely to end up something that works like you expect

Kevin Lynagh23:07:03

@hiredman Thanks for the link! I have to run this in cljs, so pipeline-async is my only option.

bfabry23:07:04

I believe pipeline-async is meant for working with callback-based libs

Kevin Lynagh23:07:44

Off-by-two isn't going to be a dealbreaker for me, I just wanted to make sure my understanding was sound.

Kevin Lynagh23:07:57

Looks like the async.clj and async.cljs implementations are the same, so presumably cljs would have the same issue.

josh_tackett23:07:34

anyone know why running lein run might overwrite my .lein-env file?

hiredman23:07:12

it looks like .lein-env is a generated cache that environ owns and you shouldn't be modifying

jeff.terrell23:07:53

I was (with much rejoicing) teaching a couple of colleagues Clojure today, and one of them asked a question that stumped me. Why is there no delete function for vectors? As in, to drop an element at an index out of the vector and move the succeeding elements backwards by an index to fill the hole?

bfabry23:07:41

because that's a very expensive operation, so you should stop and think rather than just seeing an 'easy' core op that does it

jeff.terrell23:07:51

OK, fair enough. I wondered if that was why but wasn't sure.

weavejester23:07:22

@hiredman is correct, @josh_tackett. The .lein-env file is for internal use, and shouldn’t be modified manually. It also mentions that at the top of the README.