Fork me on GitHub
#clojure
<
2018-10-17
>
dadair03:10:27

Thinking about maybe starting a local Clojure meetup and/or a regional conference. I have no experience in either (outside of attending), but I want to foster a community in my area. Are there any resources in the community (guides, templates) around planning/managing these sorts of events?

❤️ 4
Alex Miller (Clojure team)12:10:53

There is actually a guide I wrote long ago! Now if I can only find it....

dadair13:10:30

Perfect, thank you!

dpsutton03:10:36

And Alex of course

orestis05:10:41

My experience with starting a local Python UG some years ago is that it’s easy to start, harder to grow and maintain.

orestis05:10:47

In my case, I had a few Twitter followers/followees that I knew were local. I set a date/time to go grab some coffee, with the success bar being that someone other than me would show up.

👍 4
orestis05:10:45

Turns out a handful of people showed up, we chatted, then found a proper venue and set a date for an actual meetup. We also got a mailing list.

orestis05:10:11

I’ve since moved out of both Python and the city, but meetups have been happening without me, so success?

sneakypeet08:10:18

Is there a way to get to the source string of multimethods? I’m trying todo something like

(->> (methods my-multi)
     (map (fn [[k f]] (clojure.repl/source-fn f))))

borkdude11:10:12

How many JVM threads are dedicated to go blocks in standard core.async? This answer says 42 + the number of processors… (I think he means cores), but in the source code I found 8?

bronsa11:10:35

@borkdude it used to be 42+ number of processors, it's now 8 and cconfigurable

miro11:10:38

did you guys see this? looks amazing (I think heads up from Alex here on slack helped 🙂 )

cvic11:10:13

Yep, posted it in #off-topic

cvic11:10:26

But that 3% on Clojure is a bit weird. Scala is pretty low

miro11:10:46

As far as clojure is the best I wouldn't consider it being off topic 😄 And yeah, it probably also depends on how much the scala/kotlin communities were aware of the survey taking place... But overall (and regardless) I think it is a good news for clojure.

💯 8
hmaurer12:10:11

Hello! Quick question: I would like to use keywords like this in spec: ::person/first-name. Afaik I can only do this is I have a namespace that I alias to person, i.e.. (require '[... :as person]). Is there any way I can do this without creating such a namespace in another file and aliasing it?

seancorfield15:10:57

@hmaurer At work we're currently using (alias 'person (create-ns 'the.full.person)) at the top of the namespace (after the ns form) where we want to use ::person aliases without requiring a namespace. We're looking forward to whatever Rich comes up with as an alternative tho'...

👍 4
hmaurer12:10:05

@alexmiller Thanks. Is it a sensible thing to do to create namespaces for this purpose then?

hmaurer12:10:30

Or is there another approach to define attributes like this?

hmaurer12:10:04

Also, unrelated but I recall you tweeting about working on improving error messages for Clojure? Or was that someone else?

Alex Miller (Clojure team)12:10:44

One approach is to call create-ns / alias at the top of a namespace using spec. But this is an area that Rich has some future ideas about.

Alex Miller (Clojure team)12:10:13

Yes, there is a lot of error rework in the latest 1.10.0-beta3, please give it a try!

hmaurer12:10:38

Brilliant; that has always been my major pain-point with Clojure. Will give it a try! 🙂

hmaurer12:10:59

Do you have specific examples of errors that have been improved?

hmaurer12:10:06

Or is it improvements across the board?

Alex Miller (Clojure team)12:10:21

You should see it in the structure of all errors

hmaurer12:10:00

Also, while you are around, I’ve another question on Spec (if you have the time). I have to model a pretty extensive hiearchy for a project. This is a small example: https://puu.sh/BM1sE/7aaf4c8e7b.png. I was going to tag all my maps with a :type, use derive on the types to express the hierarchy and use a multispec. This seems to work o.k. so long as only “leaf types” have attributes. If the intermediate types have attributes that need to be merged into child specs it seems to become a bit messier. Do you have any advice on how to do this, and/or link to a project that takes a similar approach, or a differnt approach solving the same problem?

hmaurer12:10:08

I don’t really care how I model the hiearchy, but I want to be able to express that some things are part of a “concept”. For example, I have a bunch of maps which correspond to “failures” (similar to Cognitect’s Anomaly library). I want to be able to express that all of those are “failures”, and I was going to do so by deriving their type from :failure

Alex Miller (Clojure team)12:10:40

Well, you could not use inheritance and instead just collect attributes into containers, then model those as partial maps combined with s/merge

hmaurer12:10:41

using derive and isa? for a hierarchy of keywords seem straightforward enough; my issue is how to make it play nicely with Spec

hmaurer12:10:19

@alexmiller I need to be able to express things such as “this attribute on this map has type Compound, which means it can be anything that derives from Compound

Alex Miller (Clojure team)12:10:04

Well, I generally try to avoid doing oo things like that

hmaurer12:10:36

@alexmiller is this OO though? I just think of it as a hiearchy of concepts, which is quite natural in language and shows up in a bunch of places. For example here I am modelling an AST for first-order logic, and it’s very natural to say that a negation (i.e. not X) can be constructed by putting not in front of any formula, etc

Alex Miller (Clojure team)12:10:44

I would focus on the attributes instances have most importantly - each of those should be a known spec

Alex Miller (Clojure team)12:10:08

Then you have collections of attributes that make sense at different places in the hierarchy

Alex Miller (Clojure team)12:10:57

If they are additive then you can describe the root as a keys spec and each other node as an s/merge of parent and that node

tristefigure13:10:40

@hmaurer > This seems to work o.k. so long as only “leaf types” have attributes. If the intermediate types have attributes that need to be merged into child specs it seems to become a bit messier. This is a problem i have tackled to build map of attributes, although not for clojure.spec. https://github.com/TristeFigure/methodman

hmaurer13:10:20

oh interesting; thanks for the reference!

orestis14:10:24

I’d like for the JVM to crash and burn and exit when an exception is uncaught on the main thread, but it seems like it stays in limbo. I’m not sure if there are unjoined threads or not — how should I go about figuring this out?

ghadi14:10:46

You can set an uncaughtExceptionHandler to catch things (including a default handler)

ghadi14:10:14

Search for "dump threads" - there are multiple ways to do it

kah0ona14:10:27

Hello friends, I made a hiccup->clj-pdf convertor, written as a bunch of multimethods for all html elements like [:b] [:i] [:div] etc, which uses recursion (ie. it’s a recursive-descent parser)

kah0ona14:10:06

I think though, that this is a bit problematic due to potential overflows right? I mean, if the hiccup is very large that is

kah0ona14:10:07

could this be refactored using trampoline? I can’t really use recur, since I use a helper function that emits stuff, called from the multimethod instances.

kah0ona14:10:30

but, is it problematic in the first place? (ie. is my understanding correct about tail-call optimization?)

zilti14:10:29

Yes, if you want tail-call optimization, you need to use recur. Clojure can't tail call optimize two functions calling each other "in a loop".

kah0ona14:10:55

if you check line 179 where i parse a bold tag, i call emit-element, inside emit-element i call parse again to descend the parser

kah0ona14:10:11

so since that’s a helper fn, i can’t use recur

kah0ona14:10:17

should I use trampoline?

kah0ona14:10:44

and then let emit-element return a fn? would that work?

kah0ona14:10:02

(or a string/something terminal if it’s a leaf of the hiccup tree)

kah0ona14:10:40

not really sure if i interpret the trampoline docs correctly.

zilti15:10:35

A little different, even. You'd call parse using trampoline to begin with, and instead of calling parse or emit-element, you return a function that calls it. Trampoline will then call it.

zilti15:10:07

"Trampoline" really is a quite graphic name for it.

kah0ona15:10:21

aah yeah i see

kah0ona15:10:50

yeah i played around a bit, i think i have this working, although it sometimes doesnt seem to call the function rather return it, but that might have a different reason (ie. a bug)

zilti15:10:07

So at the beginning you call something like (trampoline parse all-args), then parse will, instead of calling, return #(emit-element "paragraph" {} all-args) and so on

kah0ona15:10:17

i’ve done that now

zilti15:10:26

That really shouldn't bug out

kah0ona15:10:19

for the input: [:div [:b "test"] "test" [:i "test"]]

kah0ona15:10:30

so i think i’ve got a bug left somewhere

zilti15:10:54

Yes, I'd guess you somewhere don't return a function, but call it instead

seancorfield15:10:57

@hmaurer At work we're currently using (alias 'person (create-ns 'the.full.person)) at the top of the namespace (after the ns form) where we want to use ::person aliases without requiring a namespace. We're looking forward to whatever Rich comes up with as an alternative tho'...

👍 4
kah0ona15:10:27

well this i’ll figure out, thanks @zilti

🙂 4
wilkerlucio16:10:58

hello, I'm trying to use Transit default handler on Clj, but that doens't seem to be working, in CLJS I can do this:

(deftype DefaultHandler []
  Object
  (tag [this v] "unknown")
  (rep [this v] (pr-str v)))

(def write-handlers
  {"default" (DefaultHandler.)})

(defn write [x]
  (let [writer (ft/writer {:handlers write-handlers})]
    (t/write writer x)))

(write {:foo (fn [] "bogus that transit cant encode")})

wilkerlucio16:10:20

and because fn is not valid, it will iuse the DefaultHandler, but on clj when I try the same it doens't work, it fails complaining it's not supported

wilkerlucio16:10:07

clj code:

(deftype DefaultHandler []
  WriteHandler
  (tag [this v] "unknown")
  (rep [this v] (pr-str v)))

(def write-handlers
  {"default" (DefaultHandler.)})

(defn write [x]
  (let [baos (ByteArrayOutputStream.)
        w    (fulcro.transit/writer baos {:handlers write-handlers})
        _    (transit/write w x)
        ret  (.toString baos)]
    (.reset baos)
    ret))

(write {:foo (fn [] "bogus that transit cant encode")})

wilkerlucio16:10:21

am I missing something?

hiredman16:10:30

a quick look through transit-clj and transit-java, it doesn't look like the string literal "default" appears in either of those libraries, which suggests it isn't a feature they have. transit-cljs seems to also not have the string "default" and seems to want the keyword :default. transit-js seems to look for "defaultHandler"

isak17:10:40

the keys are not strings, so maybe try Object or clojure.lang.AFn as a key instead @wilkerlucio

fabrao17:10:32

Hello ALL, do you think a problem solution is most performatic using reduce or recursion?

hiredman17:10:20

there are no silver bullets

Azzurite17:10:06

@fabrao depends on the specific problem and the specific solution, so without those two you will not be able to say 😉

herald17:10:49

my personal rule is reduce for simple stuff and recursion when i need more flexibility

noisesmith17:10:57

n.b. having multiple accumulator values or exiting before the end of the input are both things reduce does easily

arrdem18:10:43

yeah, reducing with a tuple accumulator is something I see all the time and reduced is right there.

markmarkmark18:10:12

reduce is the best.

markmarkmark18:10:53

I often write gnarly loop recur things and then rewrite it with reduce in a much nicer way

markmarkmark18:10:03

(nicer to me anyway)

tbaldridge18:10:39

testing performance for this is hard, since the performance problems with reduce only show up under load. Normally reduce involves more allocations and garbage creation.

tbaldridge18:10:54

Which normally isn't a problem, but I've seen it become a problem in some situations

kah0ona18:10:41

so luckily I have a nice case just above, this recursive descent parsing of hiccup data and converting it to clj-pdf datastructures 😉

kah0ona18:10:21

i’m trying to get it to work with trampoline, but i seem to hit a wall, since I’m doing this breadth first, the recursion of child elements will return a map rather than a function

kah0ona18:10:30

which makes trampoline think it’s done

kah0ona18:10:44

can trampoline work with breadth-first tree walking?

kah0ona18:10:04

have the feeling now, that i might switch to a reduce setup

kah0ona18:10:15

well i get it working without trampoline but it’ll tumble over large inputs

wilkerlucio18:10:21

@isak thanks, if I add the clojure.lang.AFn it works for fns, but what I really need is a "catch all", which Object is not doing =/

tbaldridge18:10:13

@kah0ona I don't think I've ever hit a situation where a data-structure needed to be deeper than the stack. You might be over-optimizing

wilkerlucio18:10:18

the problem is that I have a big that can be quite big, with lots of information from many different sources (worked by many different teams), so say I have around 500 keys or more in the response, one of then get me something that transit doens't understand, and that breaks the whole output

kah0ona18:10:35

hm possibly very true

wilkerlucio18:10:37

so what I need is a way to prevent that, and just get anything out for the parts it doesn't understand

kah0ona18:10:56

I just wondered how to do this with trampoline

kah0ona18:10:02

but i’ll leave it without for now

wilkerlucio18:10:39

the transit-cljs version works fine with the "default" option, but I can't figure how to do the same on transit-clj

tbaldridge18:10:42

@kah0ona I'd try using a on-heap stack instead of trampolines. So whenever you hit a branch in your tree, push it onto the stack, and when you're done with a leaf, pop an item from the stack and continue on

tbaldridge18:10:02

@kah0ona zippers work a lot like this

tbaldridge18:10:37

@wilkerlucio what happens if you write a handler for Object in Java?

wilkerlucio18:10:12

@tbaldridge I tried using the fn value example, but setting a write handler for Object didn;'t catch that, ending up in a error

kah0ona18:10:12

aah like that

wilkerlucio18:10:32

CompilerException java.lang.RuntimeException: java.lang.Exception: Not supported:

wilkerlucio18:10:13

well, seems to work with records at least, maybe fns are special in some way to this?

isak18:10:53

@wilkerlucio hm, you may need to write your own writer constructor, because the one in transit-clj does not give a way to pass in defaultWriteHandler, which is another argument it sounds like you need. See this line in transit-clj https://github.com/cognitect/transit-clj/blob/master/src/cognitect/transit.clj#L157 which calls this method in the java: https://github.com/cognitect/transit-java/blob/master/src/main/java/com/cognitect/transit/TransitFactory.java#L91

wilkerlucio18:10:27

@isak I see, seems like the default sends default handler as nil, so maybe just changing that can make it work, I'll try it out, thanks!

👍 4
wilkerlucio19:10:07

@isak thanks, the custom writer did the trick! but not without some friction... this is what I got:

(defn writer
  "Creates a writer over the provided destination `out` using
   the specified format, one of: :msgpack, :json or :json-verbose.
   An optional opts map may be passed. Supported options are:
   :handlers - a map of types to WriteHandler instances, they are merged
   with the default-handlers and then with the default handlers
   provided by transit-java.
   :transform - a function of one argument that will transform values before
   they are written."
  ([out type] (writer out type {}))
  ([^OutputStream out type {:keys [handlers transform default-handler]}]
   (if (#{:json :json-verbose :msgpack} type)
     (let [handler-map (merge transit/default-write-handlers handlers)]
       (transit/->Writer
         (TransitFactory/writer (#'transit/transit-format type) out handler-map default-handler
           (when transform
             (reify Function
               (apply [_ x]
                 (transform x)))))))
     (throw (ex-info "Type must be :json, :json-verbose or :msgpack" {:type type})))))

👍 4
wilkerlucio19:10:25

I removed a few things and add the default-handler option, this can set the default handler

wilkerlucio19:10:08

trully feels like it should be there already, you think would be ok to add this to transit @tbaldridge?

👍 4
wilkerlucio19:10:36

currently the writer from transit always sends nil to the default-handler, we could make it configuration with a key in the last argument (like in the code above)

okocim20:10:32

Just a syntax question: I know that there is a shorthand notation for making maps of namespaced keywords to values (e.g. #:foo.bar {:baz 1 :blee 2}), but I was wondering if there was also some shortcut for making vectors of namespaced keywords? (i.e., something akin to #:foo.bar[:baz :blee]). I am trying to save on typing for making a bunch of pull expressions in datomic. I suppose a simple function can fix this, but I thought I had heard about something like this already and wanted to double-check.

Chris21:10:12

When using clojure.test/run-all-tests - is there somewhere all the aggregated results can be looked up? The problem I have is that I have 1000 or so tests, and they all run and then it says 1 failed so I have to look back through a lot of log files to see which one actually failed.

Chris21:10:13

Or even just stopping at the first failure would be adequate too

hmaurer23:10:22

Quick question: is it a common / sensible pattern with Spec to conform against an or spec to get a tag, then to use that tag to dispatch on a multimethod to process the data accordingly?

Chris23:10:55

I've been doing that at times

arrdem00:10:14

If you really want open dispatch, this is a good use for multi-specs. But yes, I’ve been using s/or and core.match together for places where I want tightly controlled behavior.

hmaurer00:10:57

@U04V5T0V8 in this case it’s about modelling an AST and dispatching on the type of the nodes

arrdem00:10:28

@hmaurer My personal experience has been to favor strictness there - but ymmv

hmaurer00:10:01

@U04V5T0V8 strictness as in, s/or?

arrdem00:10:15

s/or and avoiding open dispatch, yeah.

hmaurer00:10:53

@U04V5T0V8 can you give me an example or two of cases where you would favour open dispatch?

hmaurer00:10:02

and what’s your general philosophy for strictness?

arrdem00:10:02

I don’t really have one I can communicate 🙃 I’ve been bitten in the past by the difficulty of debugging, documenting and iterating on open dispatch systems and I generally find formalized types / dispatch rules an aid to understanding. That said I literally wrote an open dispatch based API last night so 🤷

arrdem00:10:46

I guess if I had to explain, I’d say that open dispatch as a way to define extensible ASTs seems like a good way to undermine any properties you may be trying to provide within the AST - and I’d level that criticism in general against the Scheme towers of languages / nanopass compiler world.

arrdem00:10:42

Compare open dispatch as a way to define an API with good decoupling, for instance in tools.deps.alpha where it’s used to define a protocol by which arbitrary dependency descriptors can describe themselves to the resolution algorithm.