Fork me on GitHub
#admin-announcements
<
2016-01-05
>
heow01:01:17

great job josh

alandipert01:01:07

@nogeek: we invented boot partly so we could do transforms on source like that, http://hoplon.io is a cljs dialect that is translated to cljs at build time with a goal of reducing boilerplate

alandipert01:01:46

@nogeek: another example, with lein, is https://github.com/ztellman/sleight

atroche03:01:56

does the metadata reader macro just expand to wrap the following form in with-meta?

alandipert03:01:37

the reader constructs the meta map in there and attaches it to the form, doesn't go through with-meta

crocket04:01:41

I muted #C03RZGPG1, and it's better. The channel won't appear unread, and I'll be notified of mentions of myself only.

atroche04:01:03

alandipert: thanks!

nogeek08:01:25

@alandipert: very nice will try it out

dj11:01:04

Hello all. I'm relatively new to Clojure, and am learning by working through Advent Of Code. Anyway, I have a style question, if you don't mind such a newbie question. Which of these two approaches would be more idiomatic / preferable? (map (partial into []) ["hello" "world"]) or (map #(into [] %) ["hello" "world"]) thanks.

cjmurphy11:01:30

My money would be on the 2nd. With the 2nd you can see the parameter so it is easier to read.

dj11:01:03

thanks @cjmurphy - that's a good point.

mikethompson12:01:19

@dj (map vec ["hello" "world"])

dj12:01:43

gosh that is nice, thank you also @mikethompson.

meow13:01:28

Yeah, Mike's would be the canonical way to do that.

meow13:01:53

Although into is one of my favorite and quite-often used functions. So handy.

meow13:01:14

With or without transducers.

meow13:01:41

@mikethompson: Here's wishing you continued success with re-frame in 2016, btw. simple_smile

jethroksy16:01:11

@dj the partial version is apparently the prefered version, according to the style guide, but no good reason was provided

dj16:01:04

thanks @jethroksy - didn't realise there was one. Found this (assuming it's the one to which you're referring as there's a direct ref to this in it) https://github.com/bbatsov/clojure-style-guide#partial

glenjamin16:01:20

usually partial is marginally better than the equivalent #()

jethroksy16:01:38

and why is that

jethroksy16:01:05

had a long discussion about this in #C03S1KBA2 a while ago, it ended up as personal preference

jethroksy16:01:18

in #C053AK3F9 actually i think

jethroksy16:01:47

and @dj yes was referring to that

mpenet16:01:12

@glenjamin: partial will be slower in some cases

mpenet16:01:37

not that it matters most of the time, but it might be relevant depending on your usage

donaldball16:01:47

I thought the partial speed difference went away in 1.7?

meow16:01:03

It kind of depends on the context, though. For example, I usually use partial when the argument that I'm partialing in is some computed value that I don't want to recompute over and over again. Especially if said function is itself going to be passed to some other function as an argument.

bronsa16:01:05

partial doesn't instantiate a new function object like #() does, but partial uses varargs and apply internally for >4args, which is slower

mpenet16:01:24

@glenjamin: what @bronsa said

bronsa16:01:25

@donaldball: partial was unrolled for <4 args in 1.7

donaldball16:01:35

Thanks for the clarification

meow16:01:34

The original context of these questions is really odd, I just want to add. While valid code, neither example is something I would expect to see in the wild when you can just do (map vec ["hello" "world"])

meow16:01:49

And I say that as someone who loves the into function and uses it a lot. 😉

jethroksy16:01:30

but the question between choosing partial or #() actually comes out a lot

meow16:01:01

Interesting. I see them as fairly different beasts.

mpenet16:01:47

personnaly I used to prefer partial but I now find that #() reads better ...

meow16:01:10

Bearing in mind the >4 args issue, I see partial as just a way to shorten up an existing function by partialing in early arguments that aren't going to change in the given context.

mpenet16:01:13

it's also more common in clj code in the wild

meow16:01:46

Whereas #() is just syntactical sugar for an inline defined function

jethroksy16:01:20

I agree with @meow in terms of usage

meow16:01:46

I tend to just go with (fn [foo bar baz] ...) unless the #() form is super easy to understand.

jethroksy16:01:04

especially with reduce

meow16:01:28

and then you also have the limitation that you can't easily return a vec from the #() form

meow16:01:08

after getting tripped up by that its just easier to (fn []) the darn things

jethroksy16:01:40

I think its just function composition is just prefered

jethroksy16:01:51

that's why partial seems like the more popular choice

meow16:01:57

on the other hand, if the (fn []) version just adds noise then I use #() - its all about clarity

jaen16:01:59

Well, Clojure not being statically typed can't have as nice partial application as Haskell can - partial is nice and all, but usually when it's not longer to write out than a function.

jaen16:01:33

My first reflex is to use partial because partial application seems natural to me and then I have to backtrack because it's too verbose.

meow16:01:44

unless there is a performance issue, to me the act of coding is an act of communication and clarity of the code for both others and my future self is of utmost importance

jethroksy16:01:20

there we go again: personal preference hahaha

jethroksy16:01:57

but yes ultimately it's still whatever floats your boat

danstone16:01:00

partial is useful if you have an indeterminate amount of args to be applied simple_smile

jethroksy16:01:23

you could accomplish the same with %& in #()

danstone16:01:36

@jethroksy: I did not know that!

jaen16:01:10

Yeah, it's always bikeshed this, bikeshed that with programming languages : D

ddellacosta16:01:11

jaen, seriously re: partial application…always disappointed using partial in Clojure. haha

danstone16:01:17

does %& bind to a collection though, or does it splice the args?

jethroksy16:01:26

ends up in a list

jethroksy16:01:44

your usual destructuring rules apply

meow16:01:56

I used to code in Python where "one right way" to do something was what I liked. However, I like Clojure even more and while there does seem to be some amount of personal preference in these things I'm finding that even though I still try to do things one and only one way in Clojure that I need to vary my style of code in order to achieve the clarity and simplicity that I'm after. So these variations aren't a bad thing if handled properly in a variety of contexts.

danstone16:01:04

Thats not the same then, you would have to #(apply f x y %&) to get the same effect as (partial f x y)

jethroksy16:01:42

but doesn't partial use apply too?

jethroksy16:01:53

the end code is almost the same

danmidwood16:01:25

When I first started with Clojure, I found reading fns to be clearer to understand than partial. Mostly because I was unfamiliar with the fns that were being partialed, and because of the explicit argument list in fn. Later, as I've become more skilled with Clojure and I have a good chunk of the std-lib understood, partial seems a lot more straightforward, whereas fns seem a bit messy in comparison

danstone16:01:25

no actually partial implements multiple short arities, giving potential performance boosts. Tis bikeshedding for sure!

ddellacosta16:01:05

pkobrien, interesting point. I tend to find that how to implement something in Clojure comes down to exactly what kind of performance characteristics I want along with the shape of the data. I think the whole partial vs. #() thing is less common, in general—I don’t often struggle to figure out what the right way to do something in Clojure is.

jaen16:01:28

I guess some sugar for partial would have been nice, but alas.

ddellacosta16:01:27

jaen, re: your previous point it really seems to be less generally applicable, but if you are at all used to the Haskell way of doing things it seems more appealing…it’s just so natural to pass partially applied functions around in Haskell

ddellacosta16:01:50

so I often don’t end up using partial at all in Clojure, because the evaluation strategy is so different

ddellacosta16:01:58

it just feels a lot less idiomatic

meow16:01:00

Here's a fun use of partial from my code:

(defn rewrite
  "Returns a successor, which must be a vector or a function. If no match is
   found in the rules mapping, the original module is return within a vector."
  [rules m]
  (or (rules m) [m]))

(defn rewriting
  "Returns a rewriting transducer."
  [axiom rules generation]
  (map (partial rewrite (get-rewriting-rules axiom rules generation))))

jethroksy16:01:11

@danstone: don't think there's performance difference at all

danstone16:01:43

@jethroksy: look at the impl

jethroksy16:01:25

can you give an example where partial has better perf

danstone16:01:46

functions returned from partial don't use apply for small numbers of arguments

meow16:01:51

@ddellacosta: Interesting indeed. I tend to think in terms of the API that I would enjoy using and worry about performance later. And I've only been doing clojure for 8 months with a mostly OO background and no real functional experience so early on it was a real struggle for me.

jethroksy16:01:09

ah ok i see how this works now

meow16:01:15

And here's how much I like into. Built a whole bunch of uber over it:

(defn produce
  "Returns a lazy sequence of colls from a recursive, axiomatic,
   transformative process."
  [seed prep-f get-xf]
  (letfn [(process
            [coll]
            (lazy-seq
              (when (seq coll)
                (let [new-coll (into (empty coll) (get-xf coll) (prep-f coll))]
                  (cons new-coll (process new-coll))))))]
    (process seed)))

jethroksy16:01:33

there was some mention of how slow get was too yesterday

jaen16:01:43

@ddellacosta: I suppose that's a sensible POV. I'm just probably wishing that you could do something closer to map (* 2) than to (partial map #(* % 2)).

mpenet16:01:02

@danstone: #() never uses apply ... so I don't see how it could be faster

mpenet16:01:12

try to expand #(...)

danstone16:01:27

@mpenet: We were talking about when you have an indeterminate amount of args to be applied to the partial function

jethroksy16:01:34

you'll need it when you have variable args

mpenet16:01:38

ah nevermind

jethroksy16:01:59

its just whether the perf boost from bikeshedding > eval-ing the partial fn

danstone16:01:32

In my mind its the only practical difference, everything else is just clarity/readability/subjective

meow16:01:22

I have learned to think twice when I find myself writing a function with >4 parameters.

jethroksy16:01:33

^ yes this is key

meow16:01:45

If its a top-level thing that rarely gets called, that's one thing. But a low-level function that gets called a lot, or will be called using partial or apply == not good.

jethroksy16:01:22

sometimes i end up having to do a whole load of destructuring in my functions

jethroksy16:01:37

and wonder if there's a better way to access the params i need from the map

meow16:01:21

@jethroksy: got an example?

roberto17:01:38

functions with >4 arguments is always a bad idea in any language.

roberto17:01:13

in clojure, I normally try to consolidate them as maps, and use schema do document the structure. I have a feeling there is probably a better alternative.

jethroksy17:01:42

my code is pretty bad, because i did some quick hacks

meow17:01:57

In Python I never thought twice about how many parameters were required by a method.

jethroksy17:01:16

its just a simple slack integration thing, but it's hackish to allow easier syntax

jethroksy17:01:01

i also don't see code in the wild where functions are passed keywords

meow17:01:08

and in python you have named parameters so its easy to deal with lots of args

meow17:01:02

I don't think I've written any clojure functions that take keywords as args

jaen17:01:49

Oh, kwargs. Another Clojrue bikeshedding topic. I for one prefer not to use them, since they compose badly.

danstone17:01:03

I think destructing domain values in fn binding forms is a bit of a smell. Unless its literally opts or config - I would rather see a noun describing the parameter. And hopefully have as few of these nouns as possible.

jethroksy17:01:46

in this case i preferred the verbosity

meow17:01:24

if a value ultimately needs to be destructed why not do that in the parameter definition?

danstone17:01:58

Because I like to know what an argument is

meow17:01:59

I don't smell the smell there. Got an example?

jethroksy17:01:13

because yesql takes a map

jethroksy17:01:24

so i'll need the params map anyway

danstone17:01:25

@jethroksy: Give me an idea of the domain so I can write an example - what are you adding an agenda to?

jaen17:01:32

@meow: I suppose he means the fact that the parameter lacks a name, but then again you could always use :as for documentation I suppose.

meow17:01:44

I don't think you can generalize - sometimes destructed adds clarity, sometimes it takes it away. No universal rule there imo.

danstone17:01:48

@jaen :as is better than no :as thats for sure

danstone17:01:20

@meow: I agree, thats why I said domain values - the nouns of the program or the datastructures we are operating on and would like to give a name.

meow17:01:24

well, the destructed bits are getting names - again, I think it depends

danstone17:01:42

I'm not saying its never appropriate, only a sith...

jethroksy17:01:52

add-agenda takes a map with those vals

jethroksy17:01:13

adds in the db

jethroksy17:01:27

then posts to slack with a webhook

jethroksy17:01:36

obviously i failed in the code clarity part

jethroksy17:01:57

i destructed because i wanted them in the text portion for my webhook

danstone17:01:10

Sounds like you want to model an agenda

meow17:01:25

I hear what you are saying about domain values. But again, sometimes even with domain values I might want to take the focus off of what is getting passed into the function and just focus on what the function is doing to the bits and pieces and so avoiding using a name actually helps there. For me the rule is always clarity of communication and understanding, not adherence to one style of code.

danstone17:01:28

And have fns like (db/add-agenda! db agenda)

danstone17:01:46

@meow: I agree

jethroksy17:01:57

(db/add-agenda!) is generated by yesql

jethroksy17:01:05

it's arity 1 and takes a map

danstone17:01:36

if you can pass the agenda straight through there is no point, unless you want the clarity of documentation, arg names etc

jethroksy17:01:59

what better representation of agenda could there be, if not a map?

danstone17:01:08

the point is it should be clear what a function operates on. Sometimes that really is a map with some set of keys in it. Other times it is a value you are modelling in your domain.

danstone17:01:28

@jethroksy: a map is fine, I am just saying give it a name, and surface that name in your api

danstone17:01:55

so a user (or you in 5 months) can say, "oh that takes an agenda". "These functions operate on agendas" and so on.

jethroksy17:01:57

like params should probably be agenda

danstone17:01:08

Useful tool to limit the amount of context one has to keep in her/his head

meow17:01:11

what can be better than a map? sometimes a defrecord and protocols

danstone17:01:03

@jethroksy: And then maybe a schema Agenda, and a constructor agenda with a nice docstring.

danstone17:01:44

That is just my personal preference, I gotta go, but hopefully it helps you see things in a different way?

jethroksy17:01:57

yeah schema would actually be really nice

jethroksy17:01:01

thanks for the feedback!

jethroksy17:01:40

its just one of those build in a few hours, push and forget projects, but i'm not pleased with the way i've gone about it

meow17:01:13

I'm constantly revising my code. That's the only way to improve.

jethroksy17:01:21

need to start reading more code in the wild

roberto17:01:26

I never like any of the code I wrote in the past.

danstone17:01:26

As god-emperor Rich once said - Nothing much really matters for small programs that will just be hacked out the door and forgotten about

meow17:01:56

I'm brutal on my own code. I don't often like the code I wrote the day before. But I'm okay with that and expect it. Coding for me is more like writing or art - I expect to do multiple drafts.

jethroksy17:01:06

I wish I had that liberty: where i work at i'm churning out about 1 slack integration a day

jethroksy17:01:26

building a larger scale one soon, that's where i get my chance

jjttjj17:01:24

i'm having trouble deciding on the extent to which i should avoid internal state in a library i'm working on. If I have an internal id, that basically serves as a static field that is incremented every time a library operation is performed, is it fine just to define that as a top level atom in the library and increment it whenever the function is called, or is it better to leave that to the user of the library to take care of?

tgk17:01:51

@jjttjj: I think you might run into problems if someone uses your library and e.g. uses some of the namespace reloading tools commonly used for interactive development.

tgk17:01:09

I definitely adds a lot of complexity for people using and reasoning about your lib

jjttjj18:01:09

@tgk: cool thanks!

glenjamin18:01:00

jjttjj: you might want to look into UUIDs for that purpose

jjttjj18:01:43

glenjamin: unfortunately i'm depending on a java lib that needs an int for the unique idea

jjttjj18:01:47

*unique id

dj18:01:50

really interesting discussion coming out of the earlier question about partial vs #(...) usage - I've learned a lot, thank you. And @meow you are not wrong to comment (https://clojurians.slack.com/archives/general/p1452010834003083) on the oddness of my question - it's mostly due to ignorance on my part, sorry.

meow18:01:53

@jjttjj: you could use a volatile inside your fn

meow18:01:47

@dj: don't be sorry or feel like I was criticizing - just trying to provide some perspective

dj18:01:03

Although the "100 functions 1 data structure" Perlis epigram has a price - I didn't know (yet) about vec, or at least had forgotten simple_smile

meow18:01:28

@dj: trust me, I've been coding for about 30 years now and learning to think functionally was not an easy switch

dj18:01:12

I agree. But even at this early stage (for me) it is enlightening and definitely worthwhile.

meow18:01:56

That's why I jumped on the chance to get a conversation going - it turned out to be a good one.

meow18:01:19

@jjttjj: avoid state, totally

jonahbenton18:01:32

@jjttjj: two related patterns: * to define the api in a protcol and use a record for state management- so any internal state, like a metric capturing library utilization is a member of the record, and the record is passed in and returned from all api calls * if the need is just for metrics, and these metrics are important for the semantics of the library, consider just building on http://metrics.dropwizard.io/

meow18:01:34

global state, especially

meow18:01:10

Here's an example from a different context but it shows one way to increment a counter inside a stateful function:

(defn gen
  "Returns a function that, when called, will call f with an incremented
   generation number and an additional context data argument."
  [f]
  (let [generation (volatile! (long -1))]
    (fn
      [data]
      (vswap! generation #(inc (long %)))
      (f @generation data))))

meow18:01:28

I hate when library authors use global atoms for state

meow18:01:58

Or rather, I hate when library authors store state globally when it doesn't need to be global.

meow18:01:17

There are exceptions, but they should be few and far between.

meow18:01:19

And when they exist, I (as the user of said library) should be glad they are there, not irritated, imnsho. simple_smile

jjttjj19:01:25

great advice everyone, thanks!