Fork me on GitHub
#clojure
<
2015-09-21
>
grav07:09:08

How come (and true nil) returns nil? Why not stick to true|false as return type?

mikera07:09:33

It helps to have the actual values used for and and or because you can then do stuff like:

mikera07:09:02

(or (some-computation) default-value)

mikera07:09:36

(and (some-condition-check) (thing-to-execute-if-true))

mikera07:09:46

And then do something meaningful with the results

colin.yates07:09:30

I use (or (wanted) default) all the time

colin.yates07:09:10

I also read somewhere a while ago that strictly boolean return values are indicated by a ‘?', i.e. nil?

grav08:09:48

ok, it seems in both the cases @mikera mentions, you could use when/when-not? (when-not (some-computation) default-value) (when (some-condition-check) (thing-to-execute-if-true))

grav08:09:23

doesn’t that have the same semantics?

luxbock09:09:11

what if you have more than two cases? the nested whens start looking ugly then

mikera09:09:13

For me it is a style thing: I prefer when and when-not for situations where you want some kind of action (mutating state, throwing an error etc.) but prefer and and or when you are computing a result from a pure expression. Also as @luxbock points out and and or generalise better for multiple tests.

danielcompton09:09:14

Just to add, this composes well with Clojure's nil punning on sequences

Pablo Fernandez10:09:35

I read that defn- was considered a mistake and that’s why we never got a def-. I tend to agree that having functions you can’t call is rarely a good thing, but have a clear distinction between the public stable API and internal implementation details of my library is useful. How do I properly communicate that?

grav10:09:43

@luxbock @mikera @danielcompton Cool, thanks for the feedback, it cleared things up a bit simple_smile

sashton10:09:54

@pupeno I've seen some projects put internal functions in an 'impl' namespace

profil10:09:27

I am doing transduce with + and the transformation consists of map. map can take multiple collections, can I use this in my case? transduce just takes one coll, how can I give it more?

mccraigmccraig10:09:27

pupeno: potemkin is useful for separating public/private API... but you can call defn- functions (@#'some.private/fn) does the trick

Pablo Fernandez10:09:18

I want the private functions to be callable, I’m not interested in proper private. I just want it to be clear which ones are which.

profil10:09:30

Does multi coll transducers exist?

profil10:09:36

or should I do something like (partition 2 (interleave coll1 coll2))?

borkdude10:09:13

hmm, why doesn't support map-indexed multiple collections

ecelis10:09:34

lol I woke up and had this word in my mind 'potemkin', now I sit at the computer and what I read first in #C03S1KBA2? ..maybe is some kind of sign

thheller11:09:48

@borkdude you can do that with map ala "(map (fn [a b i] [a b i]) [:a1 :a2] [:b1 :b2] (range))"

thheller11:09:06

if that is what you had in mind

borkdude11:09:36

@thheller: thanks, yeah, in #C03S1L9DN there was the same idea, much better thanks

denik13:09:33

does anyone know how to serialize/deserialize array maps in clojure?

denik13:09:03

(def ^:private ipm-print-method
  (get (methods print-method) clojure.lang.IPersistentMap))

(defmethod print-method clojure.lang.PersistentArrayMap
  [o ^java.io.Writer w]
  (.write w "#array/map ")
  (ipm-print-method o w))

borkdude13:09:45

@alexmiller: maybe select-keys should throw when not called with a map? (both in clojure and clojurescript) now: (select-keys [:a :b] [:a]) ;;=> {}

Alex Miller (Clojure team)13:09:32

this may be weird but select-keys will work with associative things and vector is an associative thing - from index to value

Alex Miller (Clojure team)13:09:57

I guess it still can't return anything but {} in that case but it falls through the cracks a bit

Alex Miller (Clojure team)14:09:38

if you pass it something that doesn't implement a Map, you will get an IllegalArgumentException

borkdude14:09:54

yeah ClassCastException java.lang.String cannot be cast to java.util.Map

borkdude14:09:36

I was searching for the cause of some error and then found this

borkdude14:09:42

so maybe it's worth fixing anyway?

Alex Miller (Clojure team)14:09:06

feel free to file a ticket - there is not one about this that I know of

Alex Miller (Clojure team)14:09:55

the more I think about it, I don't think it's feasible to change this - I suspect there is code that relies on the current behavior

Alex Miller (Clojure team)14:09:26

I apologize for asking you to log that

borkdude14:09:34

I wonder what the type of select-keys in core.typed is simple_smile

Pablo Fernandez14:09:24

I want to run a generated js file (from cljs) with node. How should I call node to find that file? The js file is in the uberjar, but node cannot see into jars.

profil14:09:53

Anyone got a better solution to (map - xs (cons 0 xs))? I want to return a list of the subtraction of the item in before itself

csmith14:09:46

You might want (reductions - coll (cons 0 coll))

profil15:09:22

@csmith: That wont work with coll as init?

borkdude15:09:40

@profil: that's how I'd do it (map)

csmith15:09:08

err, I think I don’t understand the question, and I meant to leave out the first coll there

profil15:09:16

I am mostly wondering about the cons, my xs is a vector, but I guess that wont matter since cons returns a seq?

profil15:09:44

@csmith: I am terrible at explaining simple_smile I want the sequence x1, x2-x1, x3-x2 ...

csmith15:09:46

You can (into [0] coll) for the secondcollection I think

borkdude15:09:19

you could use (partition 2 1 xs), but probably less efficient

thomas15:09:24

hi guys... when you use UUIDs as keys for maps... do you just turn them into strings? and use that?

ghadi15:09:51

thomas: keep them as java.util.UUIDs. UUIDs in java are value based rather than identity based, so perfect for usage in a map

ghadi15:09:36

And they're immutable and more efficient than going down into a string

thomas15:09:43

@ghadi: silly question... how do I do that?

ghadi15:09:32

(let [myuuid (java.util.UUID/randomUUID)]
  (assoc {} myuuid  42)

ghadi15:09:34

if you happen to have a uuid in hand, keep it 😃

thomas15:09:28

and how do I go a get on that ?

ghadi16:09:34

You can use any UUID to lookup in the map with a get operation or similar. Being value-based rather than identity-based means that you can use equivalent Objects (their .equals() returns true) rather than have to access with the same Object (`obj1 == obj2`)

ghadi16:09:42

(get my-map (UUID/fromString "abcd..."))

thomas16:09:07

simple_smile the from string is the on I hadn't realised

thomas16:09:49

that doesn't seem to come with a toString, but I guess I can use the java one

thomas16:09:31

well hidden

mccraigmccraig16:09:50

@thomas: also if you are using prismatic schema, schema coercion does uuids too - https://github.com/Prismatic/schema/blob/master/src/cljx/schema/coerce.cljx#L87

thomas16:09:01

str->uuid is there as well

thomas16:09:12

planning on using that as well.

mccraigmccraig16:09:43

(and yada does HTTP parameter conversion automatically, using schema coercion)

curtosis17:09:41

anyone using/familiar-with lein-resource? I cannot for the life of me figure out how to get it to run properly.

curtosis17:09:04

that is, it runs but isn't picking up the right variables.

curtosis17:09:47

ugh. there are a lot of thorns in the interaction of profiles between :uberjar and ring uberwar. It's very opaque.

Pablo Fernandez17:09:16

How do I run another lein task when running lein uberjar automatically?

denik18:09:59

how can I read a serialized array-map?

"#array/map {0 0, 1 1, 2 2, 3 3, 4 4,}"
read-string results in
(read-string "#array/map {0 0, 1 1, 2 2, 3 3, 4 4,}")
CompilerException java.lang.RuntimeException: No reader function for tag array/map, compiling:...

timothypratley18:09:03

@denik how did you get the #array/map to begin with?

timothypratley18:09:39

(pr-str (array-map 0 0, 1 1))
;; => "{0 0, 1 1}"
(type (edn/read-string "{0 0}"))
;; => clojure.lang.PersistentArrayMap
I've always thought the type of map was an implementation detail to not include in serialization... but curious how you came to the scenario, and why it matters?

denik18:09:38

@timothypratley: here’s how, by adding a tagged literal

(def ^:private ipm-print-method
  (get (methods print-method) clojure.lang.IPersistentMap))

(defmethod print-method clojure.lang.PersistentArrayMap
  [o ^java.io.Writer w]
  (.write w "#array/map ")
  (ipm-print-method o w))

(let [m (apply array-map (interleave (range 100) (range 100)))]
  (spit file-name m))

denik18:09:21

it matters because I need to serialize a large array map into a string

denik18:09:24

@timothypratley: it has a type of PersistentArrayMap b/c that’s how clojure implements short hash-maps

denik18:09:35

however try it with something longer

denik18:09:05

@timothypratley: now I get something that looks like a unordered hash-map of type serialize_array_map.core.TaggedValue

denik18:09:31

which is the current ns.TaggedValue

timothypratley18:09:06

(type (edn/read-string {:readers {'array/map #(into (array-map) %)}} "#array/map {0 0}"))
;; => clojure.lang.PersistentArrayMap

timothypratley18:09:14

seems to work fine?

timothypratley18:09:36

I don't really recommend this, just trying to show how to get tagged reading

timothypratley18:09:18

FWIW Clojure chooses when to use array-maps for good reason IMO, so overriding that seems like an anti-optimization that I don't really understand the use case for.

timothypratley18:09:40

However I'm sure there are good reasons to prefer a particular type.

timothypratley18:09:31

I don't know whether into will work for large collections either

gtrak18:09:14

> (type (into (array-map) (map vec (partition 2 (range 100))))) clojure.lang.PersistentHashMap

gtrak18:09:28

past 7 it flips to hashmap

timothypratley18:09:43

right, so if you want to force it we will need to write a better function

timothypratley18:09:45

but it might be better to simply change the writer to be more convenient

timothypratley19:09:08

so instead of serializing #array/map {0 0} serialize #array/map [0 0]

timothypratley19:09:14

then you can just apply array-map

timothypratley19:09:17

and get the type you want.

ghadi19:09:34

> If you want ordering, don't write out {}

ghadi19:09:27

prefer clojure.edn/read-string over clojure.core/read-string btw, if it's just data

denik19:09:05

@ghadi: @timothypratley

(let [m (apply array-map (interleave (range 100) (range 100)))]
  (spit file-name (seq m))
  (apply array-map (edn/read-string (slurp file-name))))
this works, are there performance considerations I should be aware of?

denik19:09:28

this could easily have over 100.000 entries

ghadi19:09:16

Should be fine. Tho don't explicitly do array-map.... let the reader return you any map it sees fit...

ghadi19:09:40

if the file looks like {1 2 3 4...} you're going to get back a persistent hash map

ghadi19:09:59

not an array map. Same data structure API, different impl

ghadi19:09:22

Sometimes I wish array-map wasn't a thing.

ghadi19:09:38

(and a tiny nitpick, (apply array-map (interleave s1 s2)) is better expressed as (zipmap s1 s2)

ghadi19:09:52

I know it was just an example code

denik20:09:44

@ghadi thanks. I need the ordering though, so a regular hash-map is no good

denik20:09:16

@ghadi yep I know about zipmap but that would return a hash-map, right? I wanted to make sure it will be an array-map

timothypratley20:09:36

@denik can you describe a bit about why you need ordering? It sounds like you might benefit from using a vector or a sorted-map instead? what sort of data are you dealing with?

devn20:09:02

mmm, pizza synthesis....

devn20:09:14

@ghadi: you gonna be at strangeloop?

meow20:09:38

for accessing the fields of a record defined using defrecord, what's more idiomatic: (:foo record) or (.-foo record)?

gtrak21:09:09

@meow: the keyword is idiomatic and just as fast.

gtrak21:09:41

faster if it ends up using reflection

meow21:09:44

from what I've read I expected the field derefing to be faster than the keyword, but using criterium it appears that keyword is actually faster

meow21:09:13

@gtrak: yeah, that's what I've seen, thanks

denik21:09:41

@timothypratley: I want to store something similar to a time series, however also preserving the ability of inserting later events at earlier points (ordering). Without that it would work as a sorted-map

meow21:09:47

any differences with ClojureScript that are worth knowing about?

gtrak21:09:09

I don't know about cljs but I'd expect it to be similarly optimized

gtrak21:09:15

there's no need for reflection though.

gtrak21:09:35

advanced-mode might not work with fields.

gtrak21:09:37

'As soon as you want your names to be accessible from outside, or you want to access names set outside, use only aset/aget'

meow21:09:04

and the concept of protocols is function based and there is no notion of something satisfying a protocol-like-thing by having a field of a certain name, right?

gtrak21:09:39

cljs has 'specify'

gtrak21:09:00

in addition to reify

gtrak21:09:40

in that case it would be modifying fields of an instance

gtrak21:09:34

in fact it will have a 'field of a certain name' that the protocol looks up to find an impl, or at least I think it did.

meow21:09:54

interesting

meow21:09:04

but cljs only

gtrak21:09:17

somewhere, I remember a special path for keyword access on records in the jvm clj compiler

gtrak21:09:25

but I can't seem to find it at the moment.

ghadi21:09:56

devn: I'll be at Strange Loop

meow21:09:41

I had thought that satisfying a protocol by calling a function that had to be implemented by a type or record might be slower than just expecting the type or record to have a field of a certain name and then accessing that field, but that also doesn't seem to be the case - calling the function seems as fast as direct keyword access, but I didn't test it extensively.

gtrak21:09:36

one thing that's slower about records is using extend/extend-type instead of inline impls of protocol methods.

gtrak21:09:43

that adds a global lookup

meow21:09:59

right, I have read about that

meow21:09:53

I also thought it might be easier on the writer of the deftype to just declare a field with a specified name, rather than having to define a function, but oh well, a small price to pay.

meow21:09:09

@alexmiller: thanks for pushing me in the direction of deftype and defrecord - that turned out to be just what I needed to improve my L-system code - I actually just specify a protocol. Works great.

gtrak21:09:20

that's where it grabs the keyword-lookup thunk which does the field access on the record.

meow21:09:24

And I learned a lot in the process.

meow21:09:52

(defprotocol Module (module [m]))

meow21:09:39

What's the naming-convention-du-jour for protocols these days: AProtocol or just Protocol?

meow21:09:32

Next I need to provide support for cellular automata. For that I want something like a Cell protocol and I want the hash and equality of the implementers of the Cell protocol to be based on the x and y value alone, not any additional fields. So I've got to learn more to work that out, but I think that should be doable.

meow21:09:16

It almost feels like OO coding again... 😉

meow21:09:27

But better.

gtrak21:09:32

would it be worse to select the keys you want to check in a function?

gtrak21:09:12

seems like a smell unless you're passing it to third-party code that relies on hash/equals

meow21:09:04

@gtrak: I'm probably not expressing myself well as this is the first time I've been using type/record/protocol in Clojure.

meow21:09:45

but what I want to do is have a set of cells that are basically represented by their [x y] position

meow21:09:05

easy enough, right. Been there, done that.

meow21:09:22

but now I want the user to be able to do a deftype/defrecord that contains fields beyond just x and y, like [x y age color]

meow21:09:13

except I still want to check instances of these against a set where set membership is based on just the x and y values

meow21:09:22

does that make sense?

gtrak21:09:36

I guess my point is more subtle, why is this not okay? (defn equal? [cell1 cell2] (= [(:x cell1) (:y cell1)] [(:x cell2) (:y cell2)])) It allows cells to have other fields, and it lets you check equality with only x and y.

gtrak21:09:14

additionally it allows them to have equals/hashcodes that are less specific to your use-case.

gtrak21:09:23

I ask because overriding hashcode/equals seems like an antipattern

gtrak21:09:01

not having to do so is part of what's nice about using clojure and persistent data structures.

meow21:09:46

It works and you can use it to create fractals and such.

meow21:09:50

To allow the items to carry extra parameters I added a Module protocol and then I have code that does:

(defn modulate
  "Returns the module or the module supplied by an object implementing the
   Module protocol."
  [m]
  (if (satisfies? Module m) (module m) m))

meow21:09:47

So it knows how to pull out the module to use in the next step of the transducing that does the lookup/replacement

meow21:09:40

Now I want to make it easier to use this same system for cellular automata

meow21:09:57

I've already done Conway's Game of Life in clojure using sets and also (just for fun) using core.async channels.

meow21:09:59

I just need to figure out how to do this in a really generic fashion using protocols and type/record

meow21:09:18

The CA stuff is just a little more tricky than the rewriting system because you want to take the set of living cells and get their neighborhood and count living neighbors and such so it makes sense to use sets and I figured I'd need to override their hash values, but I've only just started on this so I'm quickly getting into an area I don't have any experience with yet. So I'll keep reading.

meow21:09:14

Here's an example of using deftype for parametric data in the rewriting rules:

(deftype TModule [key age]
  ls/Module
  (module [_] key)
  Object
  (toString [_] (str "<" key " " age ">")))

(defmethod clojure.core/print-method TModule [x writer]
  (.write writer (str x)))

(defn type-module-example
  []
  (let [axiom [(->TModule :A 0)]
        rules {:A (fn [g w i m]
                    [(->TModule :B 0)
                     :-
                     (->TModule (.key m) (inc (.-age m)))
                     :-
                     (->TModule :B 0)])
               :B (fn [g w i m]
                    [(->TModule :A 0)
                     :+
                     (->TModule (.key m) (inc (.-age m)))
                     :+
                     (->TModule :A 0)])}]
    (ls/parametric-context-sensitive-system axiom rules)))

(comment (take 5 (type-module-example)))