Fork me on GitHub
#beginners
<
2019-02-13
>
Mitch01:02:03

So I am trying to get my head around when to use macros or not. Say I wanted to create a construct that would generate the source code for a function that passes a set of given tests and bind it to a given var. Would the idiomatic way to do this be through macros, or through a sort of string construction routine to generate the defn block, that is then read and eval'd?

hiredman01:02:57

generally macros are appropriate for altering the semantics of code by rewriting it

hiredman01:02:22

if you have code in shape Y and you want to behave as if was in shape W then a macro can do that

hiredman01:02:01

for example, if you wrote code (when* x y) normally, no matter what y would be evaluated

Mitch01:02:17

Okay, but it is not good for the case where you want to actually generate new source code?

Mitch01:02:32

That you might want to use later

hiredman01:02:07

if you made when* a macro it (when* x y) could macroexpand to (if x y nil) so y is only evaluated if x is truthy

Mitch01:02:40

Cool thanks. That jives with my intuition, but a lot of posts that tout the power of macros talk about "creating your own syntax", but that is something separate from "generating your own code"

hiredman01:02:04

there are problems that you might solve with code generation in other languages that you can solve with macros in clojure

hiredman01:02:12

for example at work our billing system is built around a finite state machine, defined in a very kind of abstract mathematical sense (more or less a set of tuples define state transitions), and then a very weird one off macro that generates a fast transition function from that

hiredman01:02:16

and also some other macros that are specialized case variants for some values from the machine

Mitch01:02:25

Yeah that would be a pain to look at in straight java probably

Mitch01:02:36

Thanks for the help. I really appreciate it

Nico13:02:00

Hello there ! I'm getting more confident now in Clojure and I'm writing a command-line tool, that takes command-line arguments. The arguments passed must influence the behaviour of some of my functions, potentially deep. My dilemma is : should I pass my argument map along my function calls, or should I store my argument map inside an atom for example, and access it from inside my functions ? (but that way some of my functions will lose their purity) Is there a standard way of doing this ? Thanks a lot for your help !

mfikes13:02:12

Other than that, I would go with the pattern of throwing an opts map in as the last argument of your functions.

mfikes13:02:48

(I'd argue for purity.)

john13:02:04

Sometimes I'll design things so some functions have an arity for the pure path (with an extra ctx or opts map) and an arity for the impure path, which grabs the global state thing and passes that in to the pure path. That way, at any point in the chain, I can test the function out with current data stored in the state atom. Once I've debugged all fns in the chain, let data take the pure path all the way through.

Nico14:02:52

@mfikes Thank you very much Mike, I'll go this way then !

mss16:02:56

hello all, what’s the idiomatic way to read the contents of an InputStream without emptying the stream? want to tap the contents but leave them intact for processing downstream

noisesmith16:02:20

you can copy the stream, or wrap it in a subclass of InputStream that allows you to observe contents as they are consumed

mss16:02:23

right now I’m doing something like ( my-stream ( "")

noisesmith16:02:27

depends on what your actual goal is

mss16:02:38

ah ya that’s what I figured. just wanted to make sure I was on the right track

mss16:02:51

just tapping the stream contents for logging

noisesmith16:02:59

on an interop level, if you are OK with observing contents as they are consumed, you can wrap it instead of copying it https://docs.oracle.com/javase/7/docs/api/java/io/FilterInputStream.html

noisesmith16:02:34

this has the advantage of being able to work on very large streams without holding the full stream in memory, while still doing some secondary action like logging

noisesmith16:02:23

sadly there's no pure interface for InputStream, if that existed the idiomatic solution would be to reify that interface, and own an InputStream which you delegate to for fetching data

noisesmith16:02:39

but you could do something similar by using proxy...

mss16:02:24

all interesting, will need to play around with that. my java-fu is def not there yet

noisesmith17:02:09

here's an example of using proxy:

noisesmith17:02:22

user=> (slurp (iproxy))
read 5 bytes starting at 0
read -1 bytes starting at 0
"hello"

noisesmith17:02:45

see how the read method now allows interjecting, while still having the original behavior

Chase17:02:57

Am I missing out on anything in the clojure world being on jdk 8 vs jdk 11?

alexmiller17:02:15

No, other than 11 is a little faster

alexmiller17:02:03

Well 11 also fixed some stuff for vms run in containers to respect the constraints of the container that was important to a lot of people

Chase17:02:37

hmmm. technically i think I run my debian distro through a vm container on my chromebook. not sure if that is the same as what you are saying. Is jdk 8 more stable in terms of clojure tooling vs jdk 11 or is that not something to worry about?

alexmiller18:02:43

more an issue with production deployments from what I understand. something like when running in a container, JVM would report the resources of the machine rather than the container, leading it to consume too much.

ghadi17:02:09

I think a lot of things have caught up to JDK>8, but I'd encourage people to use 11 as it is the latest LTS release, and mercilessly file and fix bugs where anything isn't working on 11

ghadi17:02:24

[[email protected] ~]$ java -cp clojure-1.0.0.jar clojure.main -e '(System/getProperty "java.version")'
"12-testing"

ghadi17:02:51

Clojure 1.0 ( ) on a RaspberryPi running Java 12

alexmiller18:02:05

I think Java 8 and 11 are both solid releases with a good support story (now)

alexmiller18:02:20

you may find a few issues in Clojure tools+libs using Java 11 that have not yet been addressed (core language is fine)

CyberSapiens9718:02:46

Hey guys, a strange question... I'm building a simple API (simple CRUD for a imaginary library and it's books). And i realize that a lot of things starts to become some kind of standard when i'm returning the JSON, such as the name of fields and so on. It's good practice to save those in a constant so i can use it as a "model" later, or people tend to just write directly in the HTTP routes responses? Kinda weird question i guess, because i'm not even using the REST standards, i'll read and follow it later, just trying to figure out things right now...

manutter5119:02:55

That actually sounds like a good use case for clojure spec. Write your data models as specs, and then you can validate against them and have them for documentation, etc.

Ian Fernandez20:02:09

guys, I have a list of maps

Ian Fernandez20:02:28

[{:a 1 :d 2} {:a 2 :c 3}]

Ian Fernandez20:02:17

how can I filter only maps with :a that is 1

Ian Fernandez20:02:49

(let [mapl [{:a 1 :b 2} {:a 2 :c 3}]]
   (filter #(map :a %) mapl))

seancorfield20:02:02

(filter #(contains? % :a) list-of-maps)

seancorfield20:02:59

If you do (map :a {:a 1 :b 2}) as in your code above, map will call seq on the hash map and produce ([:a 1] [:b 2]) and then evaluate (:a [:a 1]) and (:a [:b 2]) which will produce nil in both cases.

manutter5120:02:17

Wait, @d.ian.b what result are you expecting after filtering? All maps where :a has a value of 1?

Ian Fernandez20:02:29

yeah I asked wrong

Ian Fernandez20:02:48

I want all maps that doesn't has a value

manutter5120:02:52

(let [map1 [{:a 1 :d 2} {:a 2 :c 3}]]
  (filter #(= 1 (:a %)) map1))
=> ({:a 1, :d 2})

manutter5120:02:39

You want to remove maps where :a has a value of 1?

manutter5120:02:12

(let [map1 [{:a 1 :d 2} {:a 2 :c 3}]]
  (remove #(= 1 (:a %)) map1))
=> ({:a 2, :c 3})

Ian Fernandez20:02:01

I want to list all maps that does not contain value 1 from :a

manutter5120:02:07

or maybe more interesting:

(let [map1 [{:a 1 :d 2} {:a 2 :c 3} {:b 3 :e 4}]]
  (remove #(= 1 (:a %)) map1))
=> ({:a 2, :c 3} {:b 3, :e 4})

manutter5120:02:41

remove is handy, it’s like the inverse of filter

Ian Fernandez20:02:04

never saw this 😃

dpsutton20:02:54

(filter (comp #{1} :a) coll) always looks good to my eye

😀 10