Fork me on GitHub
#clojure
<
2017-01-23
>
seylerius00:01:25

Where's the EDN reader and writer in 1.9?

gfredericks00:01:55

@grav still smells like a tree. I don't know of anything that does that but I bet you could do it by hand in 10-20 lines

seancorfield00:01:26

Same place as in 1.8... what are you looking for? @seylerius

seylerius00:01:16

@seancorfield Don't know where it was in 1.8, either.

seylerius00:01:34

Requiring clojure.edn failed.

seancorfield00:01:49

If you have Clojure loaded, you have clojure.edn available -- the require should succeed.

seylerius00:01:18

ClassNotFoundException

seancorfield00:01:54

Are you sure you're loading the correct version of Clojure? What does (clojure-version) show?

seancorfield00:01:55

user> (clojure-version)
"1.9.0-alpha14"
user=> (require '[clojure.edn :as edn])
nil
-- clojure.edn is available from Clojure 1.5.0 onwards.

seylerius00:01:40

And it worked.

seylerius00:01:00

But it only contains read and read-string

seylerius00:01:09

How do I dump shit to edn?

seylerius00:01:30

Ah, found it.

neupsh02:01:40

Hi, i have a Record which implements Component's LifeCycle protocol, how do i add new methods to the record?

neupsh02:01:57

in this case, this throws error, how do i add my-method without a protocol? do i just add Object followed by (my-method... ?

bbloom02:01:21

neupsh: you need a protocol

gfredericks02:01:51

if you think a protocol doesn't fit you could just write a function that takes the record as its first arg

neupsh02:01:06

@bbloom @gfredericks so, only way to add a method in record is by declaring a protocol for that record?

bbloom02:01:31

@neupsh yes, but gary is right, you probably don’t need a method

bbloom02:01:33

just use a normal function

neupsh02:01:55

I wanted the method to 'stick' with record, instead of using it like my.namespace/my-func

bbloom02:01:59

(defn my-function [rec some-args] (blah blah (:config rec)))

bbloom02:01:15

@neupsh why do you want that? (chances are, you actually don’t want that)

neupsh02:01:21

@bbloom i have some abstraction on something that is implemented in java. it is sort of like a 'device'. I am using Record to hold the state for the device and i wanted to add some methods on the record which are actually for the device. eg: mount for the device

neupsh02:01:21

i can do, my.ns/mount-device, but since my record holds most of the state, i wanted it to be callable from the record itself: `(.mount record)

bbloom02:01:02

@neupsh what benefit does that provide you? do you need dynamic dispatch to the implementation?

neupsh02:01:52

@bbloom just easier grouping related methods (most of them are already in protocol, but there was one or two methods i wanted to add without protocol, but i will probably create a protocol for those, or just add a function)

neupsh02:01:07

thanks a lot 🙂

bbloom02:01:44

@neupsh if you’ve got other eggs to fry, feel free - but if you’re new to clojure, you may want to stop and think a bit about why you feel that’s easier. there’s significant advantages to NOT doing that

neupsh02:01:01

@bbloom can you explain what the advantages are? i am new to clojure and this is not my primary development platform. I could certainly use guidance and pointers

bbloom02:01:06

for starters, functions are first class, so you can pass them around w/o having to wrap them. methods are not first class

bbloom02:01:15

additionally, functions are stored in vars, which have better interactive development properties

bbloom02:01:25

you can redefine them without having to find every instance of the object and update or replace it

bbloom02:01:44

function names are namespaced, method names are not

neupsh02:01:02

:thumbsup:

bbloom02:01:02

if you use functions, you can more easily create alternative data to operate on

bbloom02:01:17

ie instead of an object with a particular class, any map-like data with those keys in it works

bbloom02:01:20

think mocking or testing

bbloom02:01:25

the list keeps going

bbloom02:01:29

whenever possible, prefer functions

bbloom02:01:07

the only real benefits of using methods are 1) (sometimes) perf 2) jvm interop 3) dynamic dispatch

bbloom02:01:21

and dynamic dispatch can also be accomplished in other ways

bbloom02:01:56

if you feel your namespace name is too long to type at the call site, use a single-character namespace alias

neupsh02:01:24

@bbloom this method i wanted to add is very tightly related to the recod - mount for a DeviceRecord, actually the only methods for the records are the component's start/stop and i wanted to use mount in the record itself (only because i saw mountwas very close to the device. the implementation of mount would not be very reusable as i am dealing with only 1 device and the mount method would infact delegate to some inner method of one of the object it holds in its state. Do you think this usecase is still better of with function instead of the method?

bbloom02:01:18

@neupsh how tightly related the method is logically doesn’t really matter - do you need it to be a method for jvm interop or dynamic dispatch? if so, then make it a method, if not, use clojure’s preferred unit of logical organization: namespaces

neupsh02:01:32

@bbloom thanks for advice. i dont need interop for java side. It will only be called from clojure. I will go with the function in my namespace where the record is.

seancorfield05:01:13

@simon You might find more folks willing to help at that level in the #beginners channel? That's where folks are happy to spend lots of time working through your early Clojure programs with you.

martinklepsch07:01:03

I need to concatenate two lists in a way that they are still a PersistentList afterwards. Perf not very important. Is there any shortcut to reducing into a new list?

beppu07:01:07

@martinklepsch If performance doesn't matter, maybe (list (concat a b)) ?

martinklepsch07:01:12

@beppu that will give me a list containing the result of concat...

martinklepsch07:01:31

But the idea is good, let me see if I can do something similar 🙂

martinklepsch07:01:48

(into clojure.lang.PersistentList/EMPTY (concat '(1 2) '(3 4)))
;; => ‘(4 3 2 1)
nope 😄

beppu07:01:08

timer.core> (apply list (concat '(1 2) '(3 4)))
(1 2 3 4)
timer.core> (type (apply list (concat '(1 2) '(3 4))))
clojure.lang.PersistentList

rauh07:01:13

@martinklepsch Why not just (into existing-pers-list [:foo :bar])?

martinklepsch07:01:16

@rauh because that will reverse [:foo :bar]

beppu07:01:51

timer.core> (into '(1 2) '(3 4))
(4 3 1 2)

martinklepsch07:01:51

(defn concat-persistent-list [l1 l2]
  (reduce (fn [new-list item]
            (conj new-list item))
          l2
          (reverse l1)))

martinklepsch07:01:13

that does the trick but I guess @beppu’s solution is a bit more concise

rauh07:01:12

@martinklepsch Oh yeah that's right. So then (into ... (reverse ...))?

leov11:01:36

quick question: am I allowed to have both cljc and cljs file with the same namespace?

leov11:01:39

are they merged?

pesterhazy11:01:14

sounds like a bad idea @leov

juhoteperi11:01:20

@leov Platform specific file (cljs) is used instead of common file (cljc)

leov12:01:40

thanks. It's just my fireplace.vim cannot multiplex between browser and server during eval of forms. I opened real terminal in tmux attached to figwheel as a workaround

leov12:01:08

(I see many people doing repl development with a standalone terminal, however fireplace integrated eval seems better, because you don't keep one more open window)

leov12:01:18

oh. we have a channel vim-fireplace, I can ask there 🙂

ustunozgur12:01:09

Those are all good points. Another benefit of protocols is, as opposed to their problems in tooling and interactive development, it might make the maintenance of some parts the program easier, since it serves like a TOC. But proper ns's provide the same, so it might be a matter of tooling. For example, cider has browse-ns, that list the functions associated with a namespace, along with their docs. Protocols do this without tooling. The question then is, how aggressive to create new ns's and new files? There might be some inertia doing that, which might cause a huge ns that has lots of functions in it, that make comprehension hard and maintenance a mess. Java effectively by its design forces you to separate out the separate functions by domain model for example, where as in Python for example, you can have a single module with multiple classes, encapsulating various methods, without impacting overall readability of the file. Ns pragmas a la Obj-C might help there, but I guess the proper solution is, I guess again namespaces. Could you elaborate a bit more on that @bbloom ?

qqq13:01:30

is there a channel here for emacs-org / literate clojure?

qqq13:01:45

I'm just starting it, and it feels so much more amazing than multiple files and I want a place to learn tricks / tips from

tbaldridge14:01:56

Literate programming....now there's a term I haven't heard in a long time.

qqq14:01:32

@tbaldridge: this is taking your advice ot the extreme

qqq14:01:41

earlier, I was asking about how ot manage many small files

qqq14:01:53

you suggested I get a better IDE and use it to manage a few large files

qqq14:01:02

well now, I'm using one *.org file to generate MULTIPLE files 🙂

qqq14:01:17

in fact, my entire project is one *.ORG file

tbaldridge14:01:35

The "generate" part is the fun bit of that....now your source lines won't really match up, right?

tbaldridge14:01:52

You get an error for a .clj file that was generated from a .org file...where do you go to make the edits?

qqq14:01:16

%&#* ; I have not written an elisp plugin to patch that up yet.

qqq14:01:34

but code browsing in emacs-org is so convenient to anything else I've used

qqq14:01:39

no no, it's not a problem

qqq14:01:56

when I open up a "code block" in org mode with org-special-edit, it puts that particular block in it's own buffer

qqq14:01:00

so the line numbers match up perfectly fine

Alex Miller (Clojure team)14:01:22

@martinklepsch you could try (list* (concat a b))

martinklepsch14:01:28

@alexmiller

(type (list* (concat '(1 2) '(3 4)))) ; => clojure.lang.Cons

martinklepsch14:01:41

@alexmiller would have been nice though 🙂

bronsa14:01:19

yeah I don't think that's possible unless you're willing to pay linear time prepend

martinklepsch14:01:46

@bronsa my lists aren’t too long (don’t think I’ll ever reach 100) so perf isn’t too much of an issue

bronsa14:01:07

user=> (defn lconcat [a b] (into b (reverse a)))
#'user/lconcat
user=> (lconcat '(1 2 3) '(4 5 6))
(1 2 3 4 5 6)
user=> (class (lconcat '(1 2 3) '(4 5 6)))
clojure.lang.PersistentList

martinklepsch14:01:17

(defn concat-stack [l-front l-back]
  ;; (into '(4 5) (reverse '(1 2 3))) ; => '(1 2 3 4 5)
  (into l-back (reverse l-front)))
🙂

bronsa14:01:19

this works but it's O(n) on the length of a

martinklepsch14:01:39

not very pretty but ¯\(ツ)

bronsa14:01:38

@martinklepsch another option would be to define foldr and then you could just (foldr conj b a)

kjw15:01:31

ertyuiop-[]=[p09876543wqer0p-=[

ejelome15:01:11

when do you guys use -> vs. ->>?

williewillus15:01:06

-> threads as the second form, ->> as the last. so it depends on the functions you're threading through and where they expect their args

ejelome15:01:12

thanks @williewillus, still needs some time to digest

cgrand15:01:21

@martinklepsch (defn concat-list [a b] (into b (rseq (into [] a)))) still O(length(a)) but the constant factor should be better than with reverse

cgrand15:01:17

also your function is “concat-stack”, can’t you use vectors instead of lists?

zennist15:01:05

Can anyone answer why this doesn't work: (defn my-* ([x y] (* x y)) ([& rest] (reduce my-* 1 rest)))

zennist15:01:52

(my-* 3) gives me 3 is not ISeqable; same with 3 or more arguments

cgrand15:01:56

@zennist because [& rest] means 0+ args (including 2).

bronsa15:01:07

@zennist that's an illegal function signature

zennist15:01:13

why would that be a problem?

bronsa15:01:27

which arity should (my-* 1 2) use?

zennist15:01:37

I hope the second clause

bronsa15:01:39

[x y] or [& rest]? they're both valid

bronsa15:01:41

there's ambiguity

bronsa15:01:45

clojure won't let you do that

zennist15:01:50

I imagine it's like a pattern matching from top to bottom - but I might be wrong (I'm a newbie)

zennist15:01:11

sorry - I meant it should use the first caluse

bronsa15:01:31

clojure has no pattern matching in the language

zennist15:01:47

actually the function works for both 0 and 2 arguments - it' s just all other cases it fails

bronsa15:01:00

@zennist no, that function won't get past the compiler

bronsa15:01:19

i don't know how you defined it in your repl, but that definition that you pasted here won't even compile

zennist15:01:19

I see - I was trying it out in clojurescript; might be inconsistency there

bronsa15:01:29

yeah cljs might not have that check

bronsa15:01:38

but it should

zennist15:01:58

so my other question here: if two clauses can't be such that they handle the same amount of args, why can [x y] and [x y & rest] coexist?

zennist15:01:02

they both can handle 2 args

martinklepsch15:01:40

@cgrand can’t use vectors, dealing with library code… didn’t know rseq, good to know, thanks!

zennist15:01:26

anyone can answer my question..?

cgrand15:01:17

@zennist these rules are arbitrary (they could have been different): you can’t have the same fixed arity twice, there’s only one varag and it must start at least at the max fixed arity. Fixed arg wins over var arg in case of ambiguity.

cgrand15:01:45

note that vararg has a cost in the packing of the args into a collection

cgrand15:01:01

hence the bias towards fixed args

bronsa15:01:18

@zennist essentially, the way clojure is implemented, optional arguments can only go at the end of a signature. My guess is that ambiguity at the end is allowed vs the ambiguity in the "middle" isn't for: - ease of implementation - a desire to make it clear that multiple arities in clojure are not pattern matching

zennist15:01:56

alright - thanks guys, I'll stop fussing over this 🙂

zennist15:01:16

at the end of day i find that sometimes to understand clojure better you have to be in the shoe of a Java dev

zennist15:01:40

unfortunately i come from more of a functional programming background so sometimes struggle...

cgrand15:01:21

that’s true it’s more or the less the same rule as for java own varargs

bronsa15:01:52

I think the wrong assumption you made is that clojure did some sort of pattern matching and that arity definition order mattered (it doesn't)

ejelome15:01:54

I think that's only true if you interop it with java or am I wrong

tbaldridge15:01:05

@zennist if you want the in-depth discussion, it's because of Java method overloading.

tbaldridge15:01:53

Clojure functions are implemented as classes with invoke(a), invoke(a, b), invoke(a, b, c), etc. Where "extra args".

tbaldridge15:01:11

So (fn [& args]) is invoke(ISeq args)

tbaldridge15:01:52

So (fn [& args] and (fn [a]) are ambiguous in dispatch

zennist15:01:01

yes I see that now

bronsa15:01:01

hmm? I don't think that's relevant

cgrand15:01:07

me neither

bronsa15:01:13

or even accurate TBH

cgrand15:01:55

there the invoke with 20 positional args + an array for the rest

cgrand16:01:24

and there’s applyTo(ISeq)

bronsa16:01:58

you could (theoretically) have

class Foo implements IFn {
Object invoke() {
 doInvoke(null);
}
Object invoke(Object a) {
 doInvoke(Cons(a, nil));
}
Object invoke (Object a, Object b) {
 // 2-args impl
}
Object doInvoke(ISeq args) {
// varargs
} 
}

cgrand16:01:52

in truth the arities of a fn don’t change the way it’s invoked at its callsite

bronsa16:01:26

if you don't factor in direct-linking/prim fns :)

cgrand16:01:54

and protocols methods 🙂

bronsa16:01:07

and keyword lookups?

bronsa16:01:15

altho they don't really have arities..

cgrand16:01:28

they do have!

bronsa16:01:55

well, not user-extensible ones

zennist16:01:22

I'm a bit confused now - @bronsa so does your example show that there isn't really any ambiguity for 2 args case?

cgrand16:01:26

ok you can have any arity you want as long it’s one or 2

bronsa16:01:41

@zennist I'm saying that there's not a technical reason (that i'm aware of) why clojure doesn't let you do it, rather, it's likely an implementation detail/a philosophical reason

tbaldridge16:01:41

@bronsa but in this example: what should I dispatch for (foo 1): (fn [a] ...) (fn [& a] ...)

tbaldridge16:01:05

I could ether call it as (foo 1) or (foo '(1))

zennist16:01:09

according to previous argument: fixed argument clause wins I suppose

bronsa16:01:19

@tbaldridge that's clearly 1 argument tho

tbaldridge16:01:02

Right, so it's an argument in favor of precedence dispatch. That's valid.

bronsa16:01:03

you can't pass varargs around as seqs w/o using apply anyway

qqq16:01:09

@tbaldridge : I better see the point you were making earrlier now -- things become bad when I have multiple blocks in org mode which all go to the same *.clj file -- in this case, editing each block in its own buffer does not sve me

bronsa16:01:17

so I don't know why you'd expect that to be a problem

tbaldridge16:01:03

Let me back up and say I agree with @bronsa with an additional dispatch in every invoke you could implement the support of something like (fn ([a b c] ...) ([& args] ...)) where the fixed arities always win out. But that would cost another branch

tbaldridge16:01:30

IIRC, Pixie supports that sort of behavior, but only because ifs are free (as long as the code always takes the same branch).

bronsa16:01:43

well, to play devil's advocate here, we already pay that

bronsa16:01:48

RestFn does that double dispatch already

bronsa16:01:30

I really think it's just not legal clojure for philosophical reasons, not performance ones

bronsa16:01:04

my POV is that if that worked, people would get the impression that clojure did pattern matching

cgrand16:01:19

my bet is on implementation detail/familiar semantics rather than philosophical

bronsa16:01:26

well or that yeah

bronsa16:01:37

just not performance/technical

bronsa16:01:21

I think we've already had this discussion already over IRC years ago, it's one of these things :)

cgrand16:01:22

one loop to build methodArray. If we wanted to allow [& _] and [_] to coexist we would need either two loops (one for fixed args, one to fill the blanks) or slightly more involved code

bronsa16:01:47

btw I refute the "familiar semantics" argument by virtue of java allowing void foo(Object.. x); void foo(Object a, Object b) to coexits in a same class/interface

tbaldridge16:01:26

huh, I always assumed the compiler overrode invoke for known fixed arities

cgrand16:01:28

@bronsa you can have foo(Object.. x); void foo(Object a, Object b) but the only way to call the first is as a foo(new Object[] { x }) not very vararg

bronsa16:01:51

@tbaldridge what do you mean? it kinda does

bronsa16:01:24

it just doesn't emit all the 20 invoke arities if you have a vararg arity -- it fallsback on RestFn doing its magic & just implements doInvoke

tbaldridge16:01:51

Right, so you don't pay any cost on the fixed arities, right?

bronsa16:01:15

(this stuff is confusing to think about :))

tbaldridge16:01:22

I have a new kid in the house and don't sleep much, I understand now 🙂

bronsa16:01:46

ah, congratulations!

Pablo Fernandez16:01:38

How can I see what the current leinengen profiles are programmatically?

borkdude17:01:11

I’m trying to catch a parsing exception from clojure.data.xml, but this doesn’t work:

(try (xml/parse-str "<hello>\u0000</hello>")
       (catch Exception e
         (println “nope”)))
The exception is still thrown.

jr17:01:51

try to catch Throwable

borkdude17:01:14

Still the same

jr17:01:32

I don't think that code is throwing an exception. It seems to be swallowing the exception and printing an error

borkdude17:01:08

It’s a bit weird. The REPL behavior seems to be different than from lein run.

borkdude17:01:02

I isolated the exception in this project: https://github.com/borkdude/xml-exception The problem seems to be in the combination of clojure.data.xml and [com.fasterxml/aalto-xml “1.0.0”]

borkdude17:01:31

When you do lein run the exception appears, but you can also trigger it from the REPL by calling do-it

borkdude17:01:54

I guess it has to do with laziness

beppu19:01:41

@borkdude I've been playing with it, and it's weird. I have no idea why that's not catchable.

beppu19:01:59

Where is the laziness in that code? xml/parse-str ?

beppu20:01:56

@borkdude I caught it.

(try (xml/parse-str (seq  "<hello>\u0000</hello>")) (catch Exception e e))

beppu20:01:13

Laziness was a good clue.

ustunozgur20:01:33

Nice (!) interview question. 🙂

beppu20:01:55

You like to torture your interviewees, eh?

pelletier22:01:54

is there a thread-local version of with-redefs? trying to adapt some test code that relies on it in order to run tests in parallel

bfabry22:01:48

though that requires the vars to be marked dynamic I guess

pelletier22:01:14

yes, i'm trying to avoid that

wei22:01:06

trying to make a control structure to do this: try a request with a token. if the request fails, refresh the token and try the request again. can anyone recommend a good flow?

wei22:01:39

(the context is oauth and I’m dealing with refresh tokens)

hiredman23:01:23

(if-let [result (request token)] result (request (new-token)))

pesterhazy23:01:00

(or (request token) (request (new-token)))

wei23:01:30

ah thanks. think I was overcomplicating it