Fork me on GitHub
#clojure
<
2016-09-08
>
quoll00:09:04

Sorry, just got back from dinner. I’ll be considering other approaches as things evolve, but for now the eval/fn is ideal. Thank you @gfredericks. I just needed to unquote the x, since it may be something like:

(let (x (gensym)
      the-list (build-list-using-symbols x)
      f (eval `(fn [~x] ~the-list))]
  (f 1)
  (f 2)
  #_etc)

gfredericks00:09:25

Yeah that works too

fenak00:09:26

hey guys, i have a function to generate a timestamp for a file (like 2016083112…).. i think that is a good function to be on a utils ns or something.. what do you guys do usually?

fenak00:09:36

do you have a utils.clj somewhere?

quoll00:09:09

Yes! In every project! 😆

hiredman00:09:59

it is very common

hiredman00:09:41

I would recommend from the very beginning trying to be more specific and break things out in to distinct namespaces

hiredman00:09:01

something.utils.files or something

fenak00:09:48

i already have a something.files that’s responsible for writing files to local or S3

hiredman00:09:56

once you have a few thousand lines in a utils namespace, it can become hard to muster the effort to break it up

fenak00:09:24

i could put there and not have a utils file for now?

hiredman00:09:56

sure, put it where ever you like

hiredman00:09:31

in general I am in favor of more shorter namespaces

hiredman00:09:56

shorter in file length, not in namespace name length

fenak00:09:11

and what about function names? it’s called “timestamp”, but it’s not pure as it gets data from the sysclock.. i read somewhere (maybe stuart sierra) that this kind of fn should have a verb… like “get-timestamp” or “create-timestamp”..

fenak00:09:23

is that common sense around?

lvh01:09:27

Huh; I’m getting:

Exception in thread "main" java.lang.AssertionError: The names in #{__hash __meta __hasheq __extmap} cannot be used as field names for types or records., compiling:(datascript/db.cljc:369:1)
… but only in the latest -alpha12.

Alex Miller (Clojure team)01:09:27

There was a change in defrecord hash caching in alpha12

Alex Miller (Clojure team)01:09:51

Would love to see something that can repro

Alex Miller (Clojure team)01:09:26

Unless data script is using hasheq or hash already

lvh01:09:33

it’s the “obvious” thing: it defines a record with hash

lvh01:09:42

it uses hash in two places: one is a defrecord, the other is a deftype

lvh01:09:48

I think the only problem is with the defrecord

Alex Miller (Clojure team)01:09:23

Records use prefix for special fields

Alex Miller (Clojure team)01:09:08

deftype won't matter but they shouldn't do that either

Alex Miller (Clojure team)01:09:04

Feel free to file something or I will do so tomorrow

lvh01:09:25

that record implements IHashEq; I’m not sure if they should just … rename that? I mean, they do really want to make it hashable

lvh01:09:34

I’m filing something against datomic; do you mean on the Clojure JIRA?

lvh01:09:16

so it seems like they’d want to do something different depending on the Clojure version

kenny01:09:52

Yup, this exception is also blocking us on upgrading to alpha12

hueyp01:09:28

I’m trying my first pipeline-blocking thing with core.async … but its doing things one-by-one if I collect the result … e.g.

(let [foo (a/<!! (a/into [] results))]
      )

hueyp01:09:39

without the into I get the desired parallelism

lvh01:09:03

Is there any chance of flatland/ordered getting into core? it seems to have a weird thing going on with maintenance (PRs get review comments, but then not merged indefinitely)

lvh01:09:29

It probably doesn’t need an awful lot to be of a quality and stability suitable for core

lvh01:09:38

I could see if you’re not really interested in more data structures though

Alex Miller (Clojure team)02:09:52

@lvh re the hash stuff, I would like to get more information on whether this practice is more widespread before making any decisions

lvh02:09:02

makes sense 🙂

lvh02:09:08

That’s what made me think of flatmap btw

Alex Miller (Clojure team)02:09:12

re ordered, I think it is used enough that it is a good candidate for a contrib

lvh02:09:14

err, flatland/ordered

lvh02:09:19

(it does not use whatever)

Alex Miller (Clojure team)02:09:54

I have only needed it rarely, but when you need it, you need it

Alex Miller (Clojure team)02:09:07

(I needed it last week :)

Alex Miller (Clojure team)02:09:42

the process for this is for the owner/maintainer to offer this code on clojure-dev. typically all contributors need have a CA and agree etc. I’m not looking to seek any of that effort out right now, but that is the process

hiredman03:09:12

@hueyp: the behavior of the pipeline functions can be rather complex, I forget what constraints pipeline-blocking, but my first guess would be your consumer is too slow so the pipeline is blocking on writing to out

didibus03:09:42

Is memoize thread safe?

didibus03:09:01

What happens if its a really slow operation, like 10 seconds, if a second call is made before the first call finished, does it execute the function a second time? And what happens to the cache?

hiredman03:09:47

depends what you mean by thread safe

hiredman03:09:37

the cache is a map in an atom, no effort is made to avoid duplicating work on other threads

hiredman03:09:18

memoize isn't something you want to use seriously, checkout the core.memoize library or the caches in guava

didibus03:09:20

I'm actually using the core.memoize library

hiredman03:09:34

core.memoize is entirely different from from the clojure .core/memoize function

didibus03:09:37

I guess I'm asking if threads would block, and the only the first thread would run the function, and when its done, all threads would get the cached value

didibus03:09:48

In specific to core.memoize

hiredman03:09:38

if I recall, core.memoize uses delays to achieve that

didibus03:09:41

well, to be more specific, core.memoize/ttl

didibus03:09:42

Ok, I tested it, and it seems other threads do block

didibus03:09:58

Clojure memoize does 2

didibus03:09:35

hum, or I might be testing this wrong

didibus03:09:49

ya, testing it wrong for sure

hiredman03:09:29

as I said, core.memoize uses delays (actually a custom implementation, not clojure.core/delay) to synchronize cache misses

didibus03:09:06

Ok, now I tested it properly.

didibus03:09:17

And you are 100% correct

didibus03:09:43

memoize won't guarantee the function to run exactly once, but core.memoize will

didibus04:09:24

Interesting tidbit though, if you add ^:once meta to the normal memoize, then it does only run once

didibus04:09:00

Another odity is that it looks like println isn't thread safe either. Run it in a future in a big loop, and you won't have everything on one line. The str and the newline could be interspersed

Alex Miller (Clojure team)04:09:40

println is thread safe (there is no corruption or danger from this), it’s just writing pieces of the message to a shared stream. str first if you want to avoid this.

didibus04:09:55

Well, its not corruption, but it seems like it does not guarantee that the message you pass it and the newline it adds itself will follow each other. If another println runs concurrently, its possible the msg of the first is written to out, followed by the msg of the second and then the newline of the first and then the newline of the second.

didibus05:09:11

And there's not really anyway for me to str the newline to it.

didibus05:09:12

But, I'm not sure, testing this out in my eclipse repl, maybe a bug with the out display of eclipse

didibus05:09:49

Someone else could try it: (dotimes [x 100] (future (println "test")))

Alex Miller (Clojure team)05:09:23

yes, this is all the expected behavior of the output stream in both Java and Clojure

didibus05:09:48

Hum, interesting. You're right then, if someone doesn't want that behavior, they can do (print (str "msg" "\n")) instead. I guess I would have expected the implementation of println itself to follow that pattern.

didibus05:09:53

@alexmiller Actually, that's not the behaviour of System.out.println. So I'm tempted to call this one out as a Clojure bug

Alex Miller (Clojure team)05:09:50

this is working as intended

Alex Miller (Clojure team)05:09:48

Java’s println is not the same thing - it takes a single string whereas Clojure takes many stringable things

didibus06:09:54

I guess. Can't really think why that behaviour would be desired. I'd use print for that. Feels to me like a println should guarantee that my line actually shows up on one line

didibus06:09:58

It's good to know though, I had not realized at all it behaved this way before.

danielcompton10:09:21

is a pretty good description of what’s going on

sveri12:09:22

Looking at it for me this is a typical language surprise. From a distant far I would expect it to not mess up the new lines. And even looking at why this is, from an implementation point of view, I cannot think of a reason for the implementation being that way.

mccraigmccraig12:09:11

@sveri (apply prn (iterate inc 0)) works with the current impl - it would not print anything and blow all your memory if the string was realised before printing

james12:09:38

Does anyone know the standard for naming boolean values in Clojure? It’s clear that predicate functions should end in a question mark, but what about booleans? For example, if you wanted to name a boolean that records if a transaction completed okay, would you call it is-tx-okay,`tx-okay`, tx-okay?, or something else?

sveri12:09:48

@mccraigmccraig Ok, that makes sense

sveri12:09:06

realization of lazy seqs

alexisvincent12:09:34

Has anyone tried using the Scala GraphQL library Sangria in Clojure?

Alex Miller (Clojure team)12:09:37

@james any of those is probably fine although I'd prefer in reverse order you listed

james12:09:57

@alexmiller Thanks Alex, that’s good to know.

Alex Miller (Clojure team)15:09:10

(require ‘clojure.inspector)

katox16:09:18

Hi, anyone tried Selmer template localization? I can use Tower to lookup strings passed into the template but seems weird to pass every text that needs to be translated. I can't find an example doing that. Do you know of any?

seancorfield17:09:09

@katox the TL;DR is "it depends" — we use Selmer for localized emails at World Singles and we send over half a million a day (we have used it up to nearly 3M / day). We generally have the whole template localized and only inject data, but there a few places where we still need to inject localized strings (usually status or financial information).

seancorfield17:09:15

So we have resources/emails/<locale>/<template>.html as the general pattern with the translated text, and then we have a custom (Selmer) tag for injecting a localized key (usually as part of a conditional tag pair), and the rest we localize in the setup code and provide in the data map.

seancorfield17:09:12

(we have our own localization system, with a full admin console, with security, to support a team of translators — we have about 14 languages live on 90+ websites, including RTL such as Arabic)

katox17:09:08

@seancorfield custom tag seems like a way how to do it (for me). I only have a few places where to put short tags, the rest is html boilerplate. I don't see a way how to get request in custom tag though. Do you bind a dynamic var in middleware for it?

seancorfield17:09:46

Here’s a small example from one of our templates:

{% if not data.world-matches|empty? %}
                    <tr>
                      <td colspan="4" align="center">
                        <font face="Arial, Helvetica, sans-serif" style="font-size:16px;">
                          <b>{% resource world.new_members %}</b>
                        </font>
                      </td>
                    </tr>
… {% endif %}

seancorfield17:09:39

and then the resource tag is added as a function:

(selmer/add-tag! :resource
                     (fn [[key & args] context-map]
                       (let [filter? (#{"capitalize" "sentence-case"} (first args))
                             deref? (= "deref" (first args))
                             key (if deref?
                                   (get-in context-map (map keyword (str/split key #"\.")))
                                   key)
                             transform (when filter?
                                         (resolve (symbol (str "worldsingles.mail.template/" filter?))))
                             args    (if (or filter? deref?) (rest args) args)]
                         (cond-> (if (seq args)
                                   (i18n/format-string (i18n/get-resource key (:locale context-map))
                                                       args)
                                   (i18n/get-resource key (:locale context-map)))
                                 filter? transform))))

seancorfield17:09:30

The context-map includes the locale to use.

katox17:09:14

where do you put it into the context? in a custom render page fn?

seancorfield17:09:48

We can say {% resource deref foo.bar %} to pull data out of the context-map — that is the data struct passed into the Selmer render function — it is also passed to each tag.

seancorfield17:09:38

So we say something like

(selmer/render html {:site site :user user :locale locale
                       :locale-language (i18n/get-language locale)
                       :locale-country (i18n/get-country locale)
                       :now (date/today)
                       :data annotated-data})

seancorfield17:09:54

and that hash map is the "context" that the tag receives too.

seancorfield17:09:11

No dynamic binding needed.

katox17:09:02

I see, so you get it directly from a request.

katox17:09:19

and you pass it into the render fn

yonatanel17:09:22

Is partial the best way to pass around "object methods"? Suppose I need to register an event handler function, and I have a defrecord implementing defprotocol.

katox17:09:28

@seancorfield what do you use for resource lookup? is you code based on this? https://bitbucket.org/kotarak/j18n or just plain java i18n stuff?

katox17:09:51

@seancorfield I have a working custom tag now, thanks! I'll have to think about Tower/j18n choice a bit more.

katox17:09:54

@seancorfield One thing I don't like on clj i18n stuff is that it looks like a code in Cursive (as opposed to non-localized plain strings - no hiding/in-place-substitution). Did you manage to solve this in your custom solution?

seancorfield18:09:59

@katox we’ve had to develop our own database-backed solution because we have pretty complex requirements about cascading lookups and management / access permissions for our translation team etc.

seancorfield18:09:10

We also have multiple "types" of i18n content: some is short strings, which is a more traditional i18n "solution", but we also have full, dynamic "page" content that has different editing (and cascading lookup) constraints, as well as metadata (which is tied to "events" in the application as well as being tied to both locales and site-specific data, so the cascading lookup is complex).

seancorfield18:09:39

(FWIW, we started with standard Java i18n resource files but very quickly outgrew that)

didibus18:09:45

@mccraigmccraig That use case could be handles with a different implementation of prn for when the type of the argument is a LazySeq

didibus19:09:07

@mccraigmccraig All in all too, that's the worst thing to ever want to print. And I suspect its the least used use-case of prn ever. So for all other uses, not knowing about the non atomicity of prn could lead to actual real errors in prod code.

mccraigmccraig19:09:42

it's a dumb example it's true @didibus but (->> (readmanylines) (map things) (apply prn)) is quite reasonable

didibus19:09:40

@mccraigmccraig Its reasonable, but you would also never really do (->> (readsomanylinesIcan'tevenholdtheminmemory) (map things) (apply prn))

didibus19:09:47

@mccraigmccraig Also, concatenation is not the only way to handle the race condition. You just need a synchronization strategy. Maybe prn should take a lock on the stream until it is done.

didibus19:09:21

@mccraigmccraig Actually, that example isn't reasonable. (apply prn x y z) does not put a newline between x, y and z, only at the end of it. So I'm not sure why you would read a gigantic amount of line and print them all on the same line.

mccraigmccraig19:09:16

ha, true... bad example then

novakboskov20:09:17

What is the more genteel way to do (if key (get map key) map)?

isaac_cambron20:09:19

fwiw, that seems like a bit of weird thing to say

donaldball20:09:26

(cond-> map key (get key)) is one way

jcsims20:09:39

you can give get a default value, if I remember correctly

jcsims20:09:58

if you don’t have a second branch for that if

isaac_cambron20:09:21

yeah, but the default is for when the map doesn't contain the key, not for if the key is falsy

isaac_cambron20:09:28

i think the original is probably the clearest way to state that intent

jcsims20:09:46

if there’s not a second branch in the if, a when is slightly better

isaac_cambron21:09:04

the OP wants to return map if the key is falsy

isaac_cambron21:09:55

(in a sense there's always a second branch, it's just nil sometimes. I agree when is better for those cases)

spei21:09:12

when would a key be falsy?

sveri21:09:35

when its nil

bfabry21:09:10

or false 😛

isaac_cambron21:09:50

if you know that a) falsy values are never keys in your map and b) you never provide a non-falsy key that is not in your map, then (get map key map) would work fine

novakboskov21:09:58

I like (cond-> map key (get key)), didn't guessed that cond-> would return map...

quoll21:09:01

While you can use (get map key map), if it’s a Clojure map then you could make it (map key map) because, why not confuse everyone who wants to read it?

isaac_cambron21:09:30

haha, that too. naming a map map is pretty confusing

quoll21:09:36

I think it’s awesome that the map is called map 😄

isaac_cambron21:09:36

but honestly, I think (if key (get m key) m) is super clear and adequately concise

grzm21:09:38

My lein test runs are getting quite slow. I'd like to profile it but the instructions I see on using, say jvisualvm discuss attaching to a running process. lein test starts up a jvm, runs the tests, then shuts down, right? Suggestions on how to approach this?

bfabry21:09:38

@grzm: probably simplest to just run the tests from a repl instead, then you'll have a stable process

grzm21:09:35

@bfabry makes sense 🙂

alexisvincent21:09:04

Has anyone tried using the Scala GraphQL library Sangria in Clojure?

bfabry22:09:44

is there any way to use the leiningen test selectors at the repl?