Fork me on GitHub
#clojure
<
2019-05-16
>
jaide00:05:16

With something like ring, is it normal to layer your middleware from most app specific to most general?

(-> handler
    middleware-a
    middleware-b
    middleware-c)

wcalderipe01:05:47

Yes, I normally stack them from application to configuration middlewares.

(-> handler
      api-middleware-a
      api-middleware-b
      wrap-json-response
      wrap-gzip
      wrap-errors
      ;; etc...
)
I hope that make sense 🙂

jaide01:05:02

Yes, exactly what I was trying to find out!

jaide01:05:56

If you have something that deals with authentication where would you put that?

seancorfield02:05:31

That will depend on where in the process you want the auth to apply. We've taken different approaches with different apps, depending on exactly what we need.

jaide02:05:14

That makes sense. Thanks! After a bit of trial and error the sweet spot for me is between general configuration middlewares and the APIs.

James Vickers02:05:14

Is there a macro library for supporting 'array syntax' in Clojure? If not, is it feasible to write one? How does a macro like that work - do you sort of deal with the list elements as strings and break them apart to understand what to do? This is something like what I had in mind:

(def a (double-array [1.0 2.0 3.0])
(macroexpand '(array-syntax a[0])            ; => '(aget a 0)
(macroexpand '(array-syntax a[0] = 4.0)  ; => '(aset a 0 4.0)

hiredman02:05:29

macros are code -> code transformations, the input has to be well formed code

lilactown02:05:59

You can actually cheat here by abusing the fact that the Clojure reader will read a[0] as the symbol a and the vector with one element 0

hiredman02:05:03

because there are places where you want a single thing, and the reader will not read in a[0] as a single thing

Ivan Koz02:05:11

reader macros are not extensible for this very reason, because in other lisp people started to create new syntax constructions incompatible with code as data paradigm

Ivan Koz02:05:44

correct me if i'm wrong

lilactown02:05:54

Not sure I understand why that would break anything @hiredman. If the macro reads a then [0], seems like you can key off that appropriately in the simple cases specified

Ivan Koz02:05:26

[0] is a vector of 1 element, multiple of such simple cases will make language unbearable to learn, look at scala's underscore

Noah Bogart02:05:56

Yeah, sadly, clojure doesn't allow for reader macros, unlike Racket

lilactown02:05:32

I'm not say anyone should do this 😂

Nazral02:05:31

Hi everyone, I have a memory leak problem with my clojure program, I am trying to debug it with yourkit however the heap usage does not change, I only see the ram usage increase constantly with htop, not sure how to find where the leak is

noisesmith17:05:28

yourkit has a lot more information than htop does. The VM will never allocate more memory than the maximum you configure at startup, you can use that config to make sure the vm doesn't misbehave, then use yourkit (or even visualvm) to figure out where the free ram under that limit went if it is running out

Nazral02:05:45

thank you, I found the problem, it came from me not freeing some tensorflow ressources

emccue02:05:26

I think i badly misunderstand how namespaced keywords work

emccue02:05:30

::http/routes
=> :io.pedestal.http/routes

emccue02:05:43

i am not in the :io.pedestal.http namespace

emccue02:05:35

i thought ::symbol would resolve to :my.ns/symbol

lilactown02:05:10

I bet you have aliased io.pedestal.http to http tho

emccue02:05:18

...that would do it

emccue02:05:40

ooooh that makes sense

lilactown02:05:22

That's correct if it is in the last part. If you do ::foo/bar it will look up the symbol foo as an alias in your current ns

emccue02:05:27

::alias/thing goes to :full.path.of.alias/thing

emccue03:05:17

i have a macro which reads a resource from the classpath to generate functions

emccue03:05:30

I think i need to go AOT for the program i am working on

emccue03:05:51

during compilation i cant find that file

emccue03:05:44

...okay scratch that question

emccue03:05:52

just like 9/10 times AOT isnt needed

emccue03:05:55

but i still need to figure out how to get the clojure files into the jar

emccue03:05:07

not sure where to put

<resources>
                <resource>
                    <directory>src/main/clojure</directory>
                </resource>
            </resources>

Alex Miller (Clojure team)03:05:58

if you're using a pom, I think that goes in <build>

emccue03:05:12

think that did it, thanks

emccue03:05:46

always hate working with maven, but at the end of the day it works

lilactown05:05:48

hmm. I need to read an EDN file, assoc some stuff in it, and then spit it out… while maintaining comments

yuhan05:05:27

have a look at rewrite-clj? I think that's what many tools use for that purpose

lilactown15:05:45

that’s exactly what I was looking for! thanks!

hulunote06:05:52

How to remove tag a: ?

hulunote06:05:12

In ClojureScript: (emit-str {:tag "{}html"}) ;; => "<html xmlns=\""></html>"

hulunote06:05:00

In Clojure: (xml/emit-str {:tag "{}html"}) ;;=> "<?xml version=\"1.0\" encoding=\"UTF-8\"?><a:html xmlns:a=\""/>"

Alex Davidson Bryan09:05:55

hey, is anyone here working at juxt? need a hint on how to start a repl properly in the yada repo.

Alex Davidson Bryan09:05:14

or even if you don't work at juxt 😛

mccraigmccraig09:05:33

@alexd have an ask in #juxt

mccraigmccraig09:05:22

@dominicm is a juxter iirc, and of course @malcolmsparks

Alex Davidson Bryan09:05:59

ah, didn't see that channel

malcolmsparks09:05:18

should be lein repl though. Feel free to mail me at <mailto:[email protected]|[email protected]>

malcolmsparks09:05:42

There's also a #yada channel

malcolmsparks09:05:19

@alexd asking in #yada is the best place

abdullahibra11:05:15

i'm in situation which i need to make two things depend on each other for example: 1- write something to DB 2- if step#1 success write another something how can i make both behave like if they run inside one transaction, both must be success not only one?

abdullahibra11:05:50

maybe dosync will help here?

abdullahibra11:05:23

so maybe i create a ref object with initial val = false and when event#1 success i change ref to be true and make condition to run event#2 if ref is true?

abdullahibra11:05:32

or how better to make this?

Alex Miller (Clojure team)11:05:50

This is not really a Clojure question. If db and thing 2 don’t actually support 2PC distributed transactions like XA, then you are in the world of effectively doing your own distributed transactions and managing your own rollback in the case of failure.

Alex Miller (Clojure team)11:05:55

Refs don’t help you afaict

restenb11:05:54

is there a nice way of getting data out of a map where the keys are not keywords? f.ex. get the map at the innermost level here {1 {3 {:key true}}

restenb11:05:06

don't ask how this situation occured. 😛

noisesmith17:05:38

using keys that aren't keywords is fine - Strings, Numbers, even other hashmaps or sets. You can use the hashmap itself ({"a" 0} "a") -> 0, you can use get which is the function that a keyword is actually calling when you use it as a function

noisesmith17:05:52

keywords aren't a syntax for map lookup, they are just objects that know how to use get

noisesmith17:05:55

the one thing you should never use as a hash-map key is anything with value equality that is mutable (eg. a java ArrayMap)

art11:05:43

(get-in data [1 3 :key]), no?

4
restenb11:05:13

@art yep. slaps forehead

🙂 4
abdullahibra11:05:31

@alexmiller what do you mean i need to implement rollback methods if one of the events failed, suppose event#1 success and be inserted to the DB, and event#2 failed, then i need to run the rollback method to delete the record from the db which be inserted by event#1 ?

restenb11:05:22

@abdullahibra depending on the DB you use, but usually modifications to the same row are serialized in the database, so you can safely wait for transaction #1 to finish, check the return value, then do #2

abdullahibra11:05:20

thanks guys 👍

abdullahibra11:05:46

@restenb if transaction #1 success and then try to invoke transaction #2 which failed, then i need a rollback methods implemented for event#1 to roll back

abdullahibra11:05:54

i guess that's what @alexmiller means

restenb11:05:48

yes, unless the database supports rollbacks on it's own (most don't yet)

art11:05:01

I am not quite sure I understand what you are trying to do @abdullahibra

art11:05:26

Are you writing to a single db twice?

abdullahibra11:05:38

lemme explain in more details what i'm trying to do

art11:05:44

Are you writing to different destinations sequentially?

abdullahibra11:05:18

i would like to save reference of objects into postgresql and save the actual objects into s3 for example

abdullahibra11:05:29

so i need to make 2 operations here

abdullahibra11:05:54

1- insert reference data into postgresql of the object 2- push the object itself into s3

abdullahibra11:05:34

i would like to make both run in one transaction, to make sure i pushed reference and the object, but not any of them

abdullahibra11:05:57

my try now is to handle the failure of any for any reason

abdullahibra11:05:13

@art got what i'm trying to do?

art11:05:21

Well, it’s postgresql – in that case use transaction and if s3 upload fails roll it back, no?

abdullahibra11:05:41

what i missed postgresql has roll back, but everything clear

art11:05:43

Normally if an exception occurs somewhere inside it’s rolled back implicitly.

art12:05:29

However the best rule to follow is trying to avoid writing to multiple destinations in a single user transaction because if the transaction fails because of other reasons after the file has been uploaded it needs to be deleted. In your case that does not seem necessary but still good to keep in mind.

abdullahibra12:05:52

@artpsdev good thanks 🙂

👌 4
lilactown15:05:26

anyone know how to insert a whitespace character (e.g. newline) using rewrite-clj?

lilactown16:05:20

ah, there are utilities in rewrite-clj.node

lvh16:05:48

Heya; I'm using leiningen and need some dynamic eval in project.clj to get the absolute path to the project.clj directory; I'm using:

["--verbose"
 "--no-fallback"
 "-Dclojure.compiler.direct-linking=true"
 "-H:+ReportExceptionStackTraces"
 "--report-unsupported-elements-at-runtime"
 "--initialize-at-build-time"
 "--enable-https"
 ~(fn [proj] (str "-J-Djava.security.properties=" (:root proj) "/java.security.overrides"))
 "--rerun-class-initialization-at-runtime=org.bouncycastle.crypto.prng.SP800SecureRandom"
 "--rerun-class-initialization-at-runtime=org.bouncycastle.jcajce.provider.drbg.DRBG$Default"
 "--rerun-class-initialization-at-runtime=org.bouncycastle.jcajce.provider.drbg.DRBG$NonceAndIV"]
I was expecting that fn to be called with the project map, evaluating to a string, per https://github.com/technomancy/leiningen/blob/master/sample.project.clj#L347-L348 but clearly lein native-image isn't happy, because when I run it I get:
$ lein with-profile +client native-image
Error encountered performing task 'native-image' with profile(s): 'base,system,user,provided,dev,client'
java.lang.IllegalArgumentException: array element type mismatch
I tried to debug using lein pprint, but instead it's just getting unquoted and so I get a literal function object in pprint (that might not be a bug? I'm not sure when the fn eval is supposed to happen):
:native-image
   {:name "transgressor",
    :opts
    ["--verbose"
     "--no-fallback"
     "-Dclojure.compiler.direct-linking=true"
     "-H:+ReportExceptionStackTraces"
     "--report-unsupported-elements-at-runtime"
     "--initialize-at-build-time"
     "--enable-https"
     #object[leiningen.core.project$eval493$fn__494 0x1ec22831 "leiningen.core.project$eval493$fn__494@1ec22831"]
     "--rerun-class-initialization-at-runtime=org.bouncycastle.crypto.prng.SP800SecureRandom"
     "--rerun-class-initialization-at-runtime=org.bouncycastle.jcajce.provider.drbg.DRBG$Default"
     "--rerun-class-initialization-at-runtime=org.bouncycastle.jcajce.provider.drbg.DRBG$NonceAndIV"]},
Any ideas? Do I need #= or whatever? (That wouldn't be great because I probably need the project map :))

ghadi16:05:58

@lvh ~(fn [] ....) evaluates to a function

ghadi16:05:52

never mind, special unquote handling is some lein magic

lvh16:05:35

Well, allegedly :)

CyberSapiens9716:05:31

what is the simplest job scheduler lib you guys can recommend?

CyberSapiens9716:05:08

tried tick from juxt but the scheduling feature is disabled right now

borkdude16:05:49

anyone seen this error before?

No implementation of method: :xml-str of protocol:
   #'clojure.data.xml.protocols/AsXmlString found for class:
   org.joda.time.DateTime

borkdude16:05:49

or better: does anyone has an implementation of that protocol lying around?

rickmoynihan17:05:49

does anyone know of an alternative to jvisualvm or jmc that works with adoptopenjdk-12?

rickmoynihan17:05:23

that’s not yourkit 🙂

noisesmith17:05:23

@rickmoynihan I dont' think I have your answer directly, but which set of features do you need?

rickmoynihan17:05:11

cpu profiling/instrumentation

ghadi17:05:10

I know you asked about alternatives, but what is the reason jvisualvm doesn't fit the needs?

rickmoynihan17:05:02

it doesn’t ship with adoptopenjdk … and old versions won’t (for me at least) connect to an adoptopenjdk vm

rickmoynihan17:05:31

if it worked it would fit my needs 🙂

noisesmith17:05:46

@rickmoynihan there's HPROF if you don't need a UI or are comfortable with a fronted that loads its dump files

rickmoynihan17:05:54

yeah I downloaded it and tried it

noisesmith17:05:56

are you using the jvm arg for accepting a dt socket connection?

rickmoynihan17:05:02

@noisesmith yeah I’d really like a UI… I can at a push use hprof, but it’s not as easy as clicking a start/stop button around your repl interaction

noisesmith17:05:47

if you use clj + the hprof agent you can eliminate noise like nrepl from your measurements though

noisesmith17:05:02

it's not very convenient though, agreed

rickmoynihan17:05:05

@noisesmith: no… though you’ve made me realise it didn’t even get that far… I think it’s trying to use java 12 to load itself

rickmoynihan17:05:42

this stuff used to be easy

ghadi17:05:12

seems like you need to build master

ghadi17:05:39

that's annoying

ghadi17:05:25

there seem to be other workarounds (i only scanned quickly)

rickmoynihan17:05:39

hmm thanks for that… I tried passing the --jdkhome=/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/ but it displays an error screen saying please use jdk 8, 9, 10 or 11 — which is kinda annoying as I gave it the jdk 8 home

rickmoynihan17:05:26

I also have JAVA_HOME set to jdk8

rickmoynihan17:05:19

ok installing the official release from that site, and starting it with the command: ./bin/visualvm -J-Dcom.sun.tools.visualvm.modules.startup.DisableStartupCheck=true to disable startup checks at least starts up the UI. No idea if it’ll work if I connect to something yet.

rickmoynihan17:05:43

connecting to jdk8 vms works at least if you do that

rickmoynihan17:05:47

ok you can get heap profiles on jdk12 — but you can’t access local samplers for CPU or memory 😞 — though you might be able to do it if you setup jmx 😞

ghadi17:05:07

those can be enabled at runtime IIRC

ghadi17:05:15

I forget how though

rickmoynihan17:05:40

yeah I don’t do the JMX stuff very often

rickmoynihan17:05:53

so always need to consult what you need to do

rickmoynihan17:05:12

I might be better building from master if that works

ghadi17:05:54

i don't think you need to do that

ghadi17:05:11

probably only need to flip some options on the target VM

rickmoynihan17:05:31

it likely will be… just seeing whats involved in building it from source

rickmoynihan17:05:47

ahhh ant well this is a fun trip down memory lane!

ghadi17:05:52

shave that yak

rickmoynihan17:05:45

looks like the next yak is building netbeans… I might have to leave these unshaved

rickmoynihan17:05:50

oh joy netbeans requires mercurial not and git

ghadi18:05:49

you do not need to do any of this

ghadi18:05:19

just need to connect already built jvisualvm to the target vm

rickmoynihan18:05:37

yeah i’ve aborted and am trying that now

rickmoynihan18:05:38

I just have the fear because last time I did this I really struggled with RMI port stuff… but that was through a tunnel, so should be much more straightforward this time.

sashton18:05:13

@rickmoynihan Not sure if this will help your situation, but when I’ve had network stuff get in the way of jvisualvm, I’ve added this to my startup: -Djava.rmi.server.hostname=localhost

rickmoynihan07:05:07

yeah I tried that this time too and it still didn’t seem to work

rickmoynihan07:05:42

but I think that was the solution in the past

rickmoynihan18:05:14

ugh cannot connect

rickmoynihan18:05:21

:jvm-args ["-Dcom.sun.management.jmxremote"
             "-Dcom.sun.management.jmxremote.port=9199"
             "-Dcom.sun.management.jmxremote.local.only=true"
             "-Dcom.sun.management.jmxremote.authenticate=false"
             "-Dcom.sun.management.jmxremote.ssl=false"]

rickmoynihan18:05:17

still failing to connect when trying localhost:9199

rickmoynihan18:05:04

ugh giving up for the evening

borkdude19:05:57

is it by design that clojure.data.xml preserves newlines like this?

(parse-str "<foo><bar/>\n<bar/>\n</foo>") ;;=>
{:tag :foo,
 :attrs {},
 :content
 ({:tag :bar, :attrs {}, :content ()}
  "\n"
  {:tag :bar, :attrs {}, :content ()}
  "\n")}

Alex Miller (Clojure team)19:05:24

it is by spec I believe

Alex Miller (Clojure team)19:05:35

there is a flag to change this behavior though

borkdude19:05:57

I guess so yes: > From 0.1.0-beta3 to 0.2.0-alpha1 > Preserve whitespace by default

borkdude19:05:23

is it relatively safe to use the newest clojure.data.xml? I really need the namespace stuff 🙂

Alex Miller (Clojure team)19:05:13

I've used latest extensively without any issues

borkdude19:05:25

0.2.0-alpha6

borkdude19:05:05

thanks. the zipper stuff comes in really handy in something I had to do today 😄

dpsutton19:05:09

let me know if you run into a node which has the same name as its parent. There's a patch that I think shouldn't have gone in: https://dev.clojure.org/jira/browse/DZIP-3

borkdude19:05:09

:include-node? #{:element :characters}, the question mark is a bit weird I guess. maybe it went from a boolean setting to a enumeration later?

WhoNeedszZz19:05:56

What's going on here? I'm trying to get for to work and I'm getting strange output for doing a simple (for [n (range 1 101)] (println n)) and getting this output:

(1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil 33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil 65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil 97
98
99
100
nil nil nil nil nil)

WhoNeedszZz19:05:38

No idea why that didn't collapse

dpsutton19:05:57

println returns nil. you have a collection of 100 nils. Your repl is also printing the side effect interleaved

devn19:05:19

you may be looking for (doseq [n (range 10)] (println n))

WhoNeedszZz19:05:01

Why does for behave this way?

andy.fingerhut19:05:40

for is intended to return a value that is a sequence. It isn't really intended to perform side effects like printing in its body.

WhoNeedszZz19:05:16

So then doseq is intended for side effects?

andy.fingerhut19:05:17

doseq is intended to iterate through a sequence and do arbitrary side effects on each one.

WhoNeedszZz19:05:52

So in other words, for is lazy?

andy.fingerhut19:05:10

it produces a lazy result, yes.

WhoNeedszZz19:05:48

So my reason for asking is I never actually did FizzBuzz in Clojure and wanted to see how that looked. This works as expected, but it looks pretty bad. Is there a more concise way to do it?

markmarkmark19:05:37

my first thought would be using cond instead of multiple ifs

WhoNeedszZz19:05:51

cond doesn't evaluate after something is true

markmarkmark19:05:00

I'm not sure what you mean by that

markmarkmark19:05:21

also, it's nicer to use zero? rather than (= 0 ...)

andy.fingerhut19:05:22

Neither does a daisy-chained (or nested) sequence of if expressions.

WhoNeedszZz19:05:30

"Takes a set of test/expr pairs. It evaluates each test one at a time. If a test returns logical true, cond evaluates and returns the value of the corresponding expr and doesn't evaluate any of the other tests or exprs. (cond) returns nil."

WhoNeedszZz19:05:50

@andy.fingerhut No idea what you're talking about because the above code works as expected

WhoNeedszZz19:05:22

@markmarkmark That's fair. I forgot about that shortcut, but I was referring to the sprawling structure

WhoNeedszZz19:05:58

But technically what I have is shorter

andy.fingerhut19:05:08

fizzbuzz2 below has the same behavior as fizzbuzz above does

andy.fingerhut19:05:11

(defn fizzbuzz2 []
  (doseq [n (range 1 101)]
    (cond (and (= 0 (mod n 3)) (= 0 (mod n 5))) (println "FizzBuzz")
          (= 0 (mod n 3)) (println "Fizz")
          (= 0 (mod n 5)) (println "Buzz")
          :else (println n))))

WhoNeedszZz19:05:52

Ok. Then I guess there was a typo somewhere when I tried that

WhoNeedszZz19:05:08

That's better then, thanks

markmarkmark19:05:32

I think that the natural typo to make when using cond at first is to wrap the condition and result in a pair of parens

markmarkmark19:05:44

that would cause some weird things to happen

WhoNeedszZz19:05:54

Probably what happened

WhoNeedszZz19:05:03

I also haven't written Clojure in a minute too

WhoNeedszZz19:05:11

Part of why I wanted to do FizzBuzz

WhoNeedszZz19:05:20

Yet I'm still baffled as to how so many people fail to do it

TMac19:05:21

I’m not currently in front of a Clojure machine, but I believe that if you cheat and use cl-format you can DRY it up quite a bit:

(defn fizzbuzz [n]
  (doseq [i (range 1 (inc n))]
    (cl-format true "~&")
    (when (zero? (mod i 3))
      (print "Fizz"))
    (when (zero? (mod i 5))
      (print "Buzz"))))

WhoNeedszZz19:05:52

Interesting, but the cond is more readable

WhoNeedszZz19:05:15

Not repeating anything anyway

WhoNeedszZz19:05:29

And that's wrong

WhoNeedszZz19:05:47

Unless I'm misunderstanding cl-format

WhoNeedszZz19:05:27

Also not sure why you made it variable length. FizzBuzz is explicitly [1 100]

WhoNeedszZz19:05:01

If I had to guess the biggest mistake is doing [1 100)

andy.fingerhut19:05:25

From quick testing, this is close (but not identical) to the original fizzbuzz:

andy.fingerhut19:05:28

(defn fizzbuzz3 []
  (doseq [i (range 1 101)]
    (clojure.pprint/cl-format
     true "~&~%"
     (when (zero? (mod i 3))
       (print "Fizz"))
     (when (zero? (mod i 5))
       (print "Buzz")))))

TMac19:05:54

I wish I had a real Clojure REPL in front of me (displaced Common Lisper here), but the identical logic in CL works fine

andy.fingerhut19:05:55

The difference is that it does not print numbers when neither Fizz nor Buzz are printed.

WhoNeedszZz19:05:12

Then that's not FizzBuzz

TMac19:05:56

;; This is CL; see above for the Clojure version
(loop for i from 1 to 100 doing
  (progn
    (format t "~&")
    (when (zerop (mod i 3))
      (format t "Fizz"))
    (when (zerop (mod i 5))
      (format t "Buzz"))))

Fizz
Buzz
Fizz
Fizz
Buzz
Fizz
FizzBuzz
…

TMac19:05:12

I wonder why Clojure needed the extra ~%, but now we’re getting distracted

TMac19:05:58

Oh, sorry, I see what you did with it. Not unreasonable

WhoNeedszZz19:05:27

Literally just talked about that

andy.fingerhut19:05:36

Tim's version is Common Lisp, not Clojure.

WhoNeedszZz19:05:53

CL is inclusive for range endings?

WhoNeedszZz19:05:11

But either way still wrong. It's not printing the other numbers

TMac19:05:54

Line 6 (`(println n)`) in the original solution above was cut off for me and I didn’t realize I had to expand it. Anyway, it seems like the answer to the original question is “`cond` and zero? can clean things up a bit, and avoiding repetition of the mod logic is probably more trouble than it’s worth”

andy.fingerhut19:05:04

I don't think anyone is claiming it has the same behavior as your original version. I think Tim presented it as food for thought, as a potentially interesting way to reduce the number of explicit conditions written in the code.

TMac19:05:24

(I don’t have a FizzBuzz spec in front of me)

WhoNeedszZz19:05:18

@andy.fingerhut And that is meaningless when the spec must include that functionality, but anyway

WhoNeedszZz19:05:37

And zero? might be "easier to read" for some people, but it's actually not shorter

WhoNeedszZz19:05:17

I actually prefer the cond version because it is more readable, despite being a bit longer

Jimmy Miller19:05:05

If you want a fun challenge, think about how to do fizzbuzz with no booleans (or pattern matching). I've given the challenge to a few different people and its always interesting to see what people come up with. You can assume you have a range function and can iterate over each element of it.

WhoNeedszZz19:05:29

That's interesting for sure

WhoNeedszZz19:05:54

And there is an actual solution to that?

Jimmy Miller19:05:08

There are multiple.

WhoNeedszZz20:05:45

I'm guessing it's something pretty subtle

eval-on-point20:05:52

does no boolean mean no way to check equality?

WhoNeedszZz20:05:07

That's where I'm stuck

andy.fingerhut20:05:14

This does not attempt to solve it in the way jimmy described -- just another variation similar to Tim's that does behave the same as the original:

andy.fingerhut20:05:18

(defn fizzbuzz4 []
  (doseq [n (range 1 101)]
    (let [msg (str (if (= 0 (mod n 3)) "Fizz" "")
                   (if (= 0 (mod n 5)) "Buzz" ""))]
      (println (if (= msg "") n msg)))))

noisesmith20:05:49

you can remove the empty strings from the if clauses defining msg

(ins)user=> (str nil "OK")
"OK"
(ins)user=> (str nil nil)
""
(ins)user=> (str "OK" nil)
"OK"

andy.fingerhut20:05:17

I know. I'm just not (yet) comfortable relying on such things 🙂

noisesmith20:05:17

and that doseq is so close to a dotimes...

noisesmith20:05:42

you can replace (if (= msg "") n msg) with (or (not-empty msg) n)

(ins)user=> (not-empty "")
nil
(ins)user=> (not-empty "foo")
"foo"

WhoNeedszZz20:05:17

That's another one that is technically shorter, but different readability

noisesmith20:05:53

as far as I'm concerned both not-empty and zero? are clearer - they are weak constructs so their meaning is more direct

noisesmith20:05:07

I use them even when they are longer

WhoNeedszZz20:05:09

I like using = because it's consistent with forms that don't have those shortcuts

noisesmith20:05:34

I prefer specialization when it exists in the standard library, it reduces ambiguity

noisesmith20:05:51

eg always use val instead of second/last for map entries

WhoNeedszZz20:05:26

For example, there are short forms for < and >, but not <= or >=. You have to use an additional form to negate

noisesmith20:05:31

oh you mean pos? and neg?

WhoNeedszZz20:05:56

So for >= 0 you have to do (not (neg? n))

WhoNeedszZz20:05:45

When you could have just did (>= n 0)

WhoNeedszZz20:05:01

I prefer consistency with outliers

WhoNeedszZz20:05:33

Also the former is two operations while the latter is only one

WhoNeedszZz20:05:50

Here's some DRY cleanup

Lennart Buit20:05:47

Is this cheating 😛?

😄 16
👏 8
WhoNeedszZz20:05:43

Well it's not FizzBuzz

Lennart Buit20:05:04

Hmm how do you mean, because it doesn’t print? It produces the exact sequence here: https://en.wikipedia.org/wiki/Fizz_buzz

WhoNeedszZz20:05:50

"Write a program that prints the numbers from 1 to 100. But for multiples of three print "Fizz" instead of the number and for the multiples of five print "Buzz". For numbers which are multiples of both three and five print "FizzBuzz"."

Lennart Buit20:05:47

Right, but thats trivial: (run! println (take 100 fizz-buzz)) 😉

🎩 4
Jimmy Miller22:05:10

I usually count the third param to get as kind of cheating. I usually let it slide. But ask people to rewrite without using it or or.

Jimmy Miller22:05:40

Disclaimer: I don't do this in interviews. Just for fun.

Lennart Buit22:05:03

Yeah same, I agree that 3-arity get is a cheat because it will use a conditional internally.

Lennart Buit22:05:01

It’s a fun problem to think about abstractly, like how would you write it for using Fizz for 3, Barr for 5 and Buzz for 7. And then how would you write it when this mapping was arbitrarily given as an input.

Lennart Buit22:05:43

Makes you realize what mathematical properties such a simple program really has.

john20:05:58

data first!

Lennart Buit20:05:56

Its a bit sad that the next question obviously would be: “cool, and now for 5 and 7”

WhoNeedszZz20:05:43

That would seem unnecessary. If you can't do regular FizzBuzz you aren't going to do that and if you can you can do both

WhoNeedszZz20:05:53

What's sad is how many people can't do it

andy.fingerhut20:05:33

I think Lennart's comment is that his approach can generalize for arbitrary number instead of 3 and 5, but the size of the table required gets bigger with larger number (or at least with larger values of their least common multiple)

WhoNeedszZz20:05:00

Cool, but it's not what was asked so it would fail the test

WhoNeedszZz20:05:30

The purpose of having an applicant do FizzBuzz is to establish that they can do basic programming, but more importantly, read a spec

WhoNeedszZz20:05:36

If he were to give such a solution in an interview he would fail it

Lennart Buit20:05:38

I don’t understand why, because it doesn’t print, is that what you are falling over?

WhoNeedszZz20:05:24

What is the spec?

Lennart Buit20:05:56

what are you implying here. The program I wrote correctly produces an infinite sequence of numbers with all multiples of 3 replaced by “Fizz” and multiples of 5 by “Buzz”. The only thing it doesn’t do, and was in the spec, is to limit to 100 and to print. That is however trivial to add:

(defn print-fizz-buzz
  []
  (run! println (take 100 fizz-buzz)))

Lennart Buit20:05:23

It uses the fact that FizzBuzz (or at least the replacements) repeat themselves after the least-common-multiple of 3 and 5, which happens to be 15.

Lennart Buit20:05:30

Thats why it becomes “harder” to write this replacement table for 5 and 7, because their least common multiple is 35

WhoNeedszZz21:05:34

Wow...so the spec is "Write a program that prints the numbers from 1 to 100. But for multiples of three print "Fizz" instead of the number and for the multiples of five print "Buzz". For numbers which are multiples of both three and five print "FizzBuzz"." Your code does not do this so you failed to understand the spec, thus failing the test. I don't know where the disconnect is...

WhoNeedszZz21:05:48

As stated, the point is not can you do some trivial code; it's can you adhere to a simple spec.

andy.fingerhut21:05:34

During an interview, some interviewers are looking more for how well the applicant thinks, explains, understands, and communicates, and would not fail someone automatically if you pointed out to them that their function returns a sequence, but doesn't print it out. Especially if the applicant then says "Oh, yeah, to print it out, here are the changes required".

WhoNeedszZz21:05:11

That's a poor interviewer then

WhoNeedszZz21:05:20

The whole point of the test is to check your reading comprehension

andy.fingerhut21:05:21

Your opinion differs from mine on that point.

WhoNeedszZz21:05:42

My opinion is irrelevant. The purpose of the test is well established

andy.fingerhut21:05:26

If you pass over applicants that miss minor point, but solve the meat of the problem, and quickly understand and fix the problem when it is pointed out, I think the interviewer is potentially missing out on a great candidate.

Lennart Buit21:05:34

Yeah, and on mine. I think its more important that an applicant has the ability to abstract and adapt than to follow the test to the letter.

WhoNeedszZz21:05:56

The question was clear. The question did not ask for an abstraction, thus you failed the test

WhoNeedszZz21:05:04

This is pretty amazing I have to say...

andy.fingerhut21:05:08

Great, then you are not hiring us 🙂

lilactown21:05:21

the efficacy of fizzbuzz et. al. is murky anyway, but getting the correct answer is never the point of these questions but how they approach problem solving. If you read the problem wrong the first time, I am not going to fault you for it. If you respond to feedback and correct it, you still pass.

WhoNeedszZz21:05:37

If you didn't follow the spec, which is your job as a developer, hell no

WhoNeedszZz21:05:58

I've stated multiple times what the purpose of the question is, which is well established. It's supposed to be dead simple so you can easily follow it. If you can't follow a deliberately blatantly simple spec the chances you are going to follow a complex spec is very slim to none.

andy.fingerhut21:05:30

We understand your view

🎩 4
WhoNeedszZz21:05:41

Considering that a large portion of "bugs" come from not adhering to the established spec, this is critical

WhoNeedszZz21:05:58

Which is why it is testing that

WhoNeedszZz21:05:20

Other questions are indeed looking for how the applicant reasons and not caring about the actual answer, but this isn't one of them

WhoNeedszZz21:05:14

Hiring managers that pass people that fail such tests are a huge part of the problem

Lennart Buit21:05:24

Lets agree to have different opinions here, some of us like to test the reasoning capability of applicants (and this is what we do at my company) and others like to test the way they read specifications.

WhoNeedszZz21:05:21

You're again missing it. Our opinions are irrelevant. The FIzzBuzz question tests a specific thing, which has been stated repeatedly. What you think it is testing or what you think it should be testing is irrelevant.

andy.fingerhut21:05:58

Would you agree that companies should be able to set their own hiring policies?

seancorfield21:05:20

Hiring managers that ask folks to program in an interview is a huge part of the problem 🙂

💯 12
seancorfield21:05:47

On-the-spot tests in interviews just don't work. They don't reflect real-world programming experience at all.

WhoNeedszZz21:05:07

That is an over-generalization

WhoNeedszZz21:05:17

There are certainly bad questions, FizzBuzz is not one of them

WhoNeedszZz21:05:44

FizzBuzz reflects real-world very well. CAN YOU READ A SPEC?

seancorfield21:05:46

I've been a hiring manager for two decades. I've never asked these sorts of "performing monkey" questions in interviews, and I've never had to fire anyone I've hired for an inability to program.

respatialized21:05:56

there are many hills to die on, the exact purpose of fizzbuzz is not one of them

😂 28
WhoNeedszZz21:05:26

Well that there is an Appeal to Authority fallacy so ok?

seancorfield21:05:56

I think FizzBuzz is a stupid interview question/technique. It's as simple as that.

WhoNeedszZz21:05:12

Good for you?

seancorfield21:05:53

And, to be honest, this whole discussion belongs in #jobs-discuss if it belongs anywhere at all in this Slack.

WhoNeedszZz21:05:57

I don't recall the original conversation being about whether or not it is a good idea, but what it is

WhoNeedszZz21:05:26

It could have been moved there, but the original conversation was about Clojure so it was on-topic.

WhoNeedszZz21:05:07

This is also a thread so if people don't want to read it they don't have to. You nitpicking about where this should be is unnecessary

andy.fingerhut21:05:15

Sean is a moderator on Clojurians Slack, and often helpfully points out to people where discussions most appropriately belong (or in some cases, do not)

Lennart Buit21:05:58

Yes, I would like to add that your tone can be seen as hostile, or it it least felt hostile to me. Please realise that you are on a public forum full of humans who can get their feelings hurt, and are here to wind down and relax. This is obviously not a real job interview ^^.

WhoNeedszZz21:05:27

There is no tone in text conversations, which is the issue here. I did not mean any negative "tone" and suggest that perhaps your defensiveness made you interpret what I said as such. Given the initial conversation was about adhering to the spec of FizzBuzz, stating that your proposed solution fails the spec is perfectly valid and wasn't implied as a defect with you, but just an objective statement. I'm sorry that you interpreted it that way.

Alan Thompson20:05:34

@borkdude I have 2 convenience parsers for XML here using clojure.data.xml and tagsoup