beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
Zaymon 2021-01-13T01:19:35.077Z

Is https://github.com/dakrone/clj-http the preferred http client? Just for basic requests / polling / API exploration? New to Clojure I don’t know what the go to libraries are yet

Zaymon 2021-01-13T01:25:56.077200Z

Or would http-kit be a better choice>

2021-01-13T01:42:15.077400Z

I'd say the "most common default" one is Jetty with Ring: https://github.com/ring-clojure/ring/tree/master/ring-jetty-adapter

2021-01-13T01:42:28.077600Z

Oh sorry, HTTP Client?

2021-01-13T01:47:01.077800Z

Hum, either or, if its just for basic requests / polling / API exploration I'd go with HTTP Kit client personally, because its got zero dependencies. clj-http brings a lot of dependencies.

Zaymon 2021-01-13T01:58:24.078100Z

Sounds good to me. Thanks! @didibus

2021-01-13T02:11:35.078300Z

The built in java http client is pretty good. I would recommend clj-http over http kit, especially for exploratory stuff to avoid having to decide how you are going deal with multiple threads

2021-01-13T02:29:32.078500Z

@hiredman What do you mean? This is all you need to do:

(require '[org.httpkit.client :as http-client])
@(http-client/get "<http://google.com>")

2021-01-13T02:39:24.078800Z

You are queuing up work to happen on another thread and then waiting for it, which is not the same as doing it on the same thread.

2021-01-13T02:40:21.079Z

At the very least you have this global httpkit thread spinning doing work, which it will continue regardless of what happens to your thread

2021-01-13T02:45:02.079200Z

I don't really see the issue...

seancorfield 2021-01-13T03:13:07.079400Z

We mostly use clj-http at work but there are a couple of places where we need to be able to cancel HTTP requests after a fixed timeout period and we use http-kit's client for that since clj-http doesn't seem to have a way to do that (and we're using an old version of clj-http because recent version break some things in our tests).

seancorfield 2021-01-13T03:13:37.079600Z

I like the simplicity of http-kit's client, but hiredman's point is important.

seancorfield 2021-01-13T03:17:19.079800Z

http-kit's client request pool (default): https://github.com/http-kit/http-kit/blob/master/src/org/httpkit/client.clj#L86-L89

2021-01-13T03:52:21.080200Z

For exploration that default seems fine, and then if you need to become much more serious, it lets you specify your own.

2021-01-13T04:02:21.080400Z

Also, from peeking at the impl of http-kit, the client seems to multiplex NIO, so there's only one thread doing the request, the thread-pool is for your handlers.

aratare 2021-01-13T05:52:37.083300Z

Hi there. I've come across tap&gt; and am not sure when and where it should be used. From the Clojure release note, it's said that: > tap is a shared, globally accessible system for distributing a series of informational or diagnostic values to a set of (presumably effectful) handler functions. It can be used as a better debug prn, or for facilities like logging etc. But how is tap&gt; any different from, say, normal logging? Thanks in advance πŸ™‚

seancorfield 2021-01-13T05:54:11.083700Z

It isn't intended for logging but it is awesome for debugging.

seancorfield 2021-01-13T05:54:41.084300Z

Tools like REBL, Reveal, and Portal all "listen" for tap&gt;'d values and display them.

dharrigan 2021-01-13T05:54:54.084600Z

I've been using clj-http with great success, very easy to use.

seancorfield 2021-01-13T05:55:19.085300Z

(funnily enough, I just gave a talk to London Clojurians today that featured tap&gt; and I talked about it -- the recording should be up soon)

πŸ‘ 1
aratare 2021-01-13T05:56:53.085700Z

Looking forward to it. Thanks πŸ™‚

seancorfield 2021-01-13T05:57:55.086500Z

The great thing about tap&gt; is that you can leave it in your (production) code and the attach and detach various listeners (using add-tap and remove-tap).

jaihindhreddy 2021-01-13T09:20:48.096200Z

Assuming there are no add-tap calls that do bad things, right?

seancorfield 2021-01-13T17:13:58.128700Z

Right. I was assuming no tap listeners (except when debugging).

aratare 2021-01-13T06:03:14.088600Z

Ah I see. So unlike typical (println value), tap&gt; allows you to dynamically configure how value will be handled based on which tap you're attaching, so, say, you may have a tap to print a full value in dev but a different tap to print only important bits in test. Am I getting close?

seancorfield 2021-01-13T06:21:12.089700Z

Not quite. All tap&gt;'d values go to all listeners, but the important part is that those listeners get data so they can choose to display it graphically, or in a table, or whatever.

seancorfield 2021-01-13T06:22:05.090400Z

But each tap listener can decide what to do with each value, based on its structure.

seancorfield 2021-01-13T06:23:30.091800Z

You can (add-tap println) and get the same regular printed output, or (add-tap prn) to get readable printed output, or (add-tap some-graphical-ui) to display data in a GUI. Take a look at https://github.com/vlaaad/reveal/ to see the sort of things that you can do with tap&gt; output.

aratare 2021-01-13T06:27:12.092600Z

That's handy. Now I see how one would want to use it over normal println. Thanks a lot for the explanation :D

2021-01-13T07:17:32.093Z

to be clear, I am not saying don't use http kit, or that there is something wrong with http kit, it just would not be my first choice for fiddling around. if I wanted some with no dependencies I would use the http client that comes with java, if I wanted something to use without thinking too much about it I would use clj-http

pinealan 2021-01-13T08:55:30.094900Z

how would you do fetch in clojurescript? is https://github.com/r0man/cljs-http a de facto client? or would you just use the fetchAPI or axios?

gon 2021-01-13T08:58:57.095400Z

There is also https://github.com/JulianBirch/cljs-ajax

roelof 2021-01-13T09:36:31.096600Z

A late good morning all

Christian 2021-01-13T09:55:41.097600Z

Maybe this is more of a beginner question?

roelof 2021-01-13T09:56:49.098900Z

first question of the day. If I want to ask a api several times to keep all the responses can I then do (let [answer] (repeat 3( slurp ........))) ?

popeye 2021-01-13T10:13:51.100100Z

I am running below code and getting unable to resolve jj , Does if-let not consider else?

(if-let [jj (false? false)]
           (println "as")
           (println jj))

roelof 2021-01-13T10:21:15.100700Z

it should but right now I cannot check it. I see a message on the last line that jj is not known

roelof 2021-01-13T10:21:24.101Z

@popeyepwr

Christian 2021-01-13T10:21:32.101300Z

it's only known in the true line:

(if-letΒ [jjΒ (true?Β false)]
Β Β (printlnΒ jj)
Β Β (printlnΒ "as"))

βœ… 1
simongray 2021-01-13T10:21:35.101400Z

(defmacro if-let
  "bindings =&gt; binding-form test

  If test is true, evaluates then with binding-form bound to the value of 
  test, if not, yields else"
  ....)
so no, it doesn’t bind in the else clause.

simongray 2021-01-13T10:22:14.101600Z

but what’s the point of binding it? You already know the answer. It’s falsy.

popeye 2021-01-13T10:23:13.101900Z

This is just a sample code, In production code I am assigning values

simongray 2021-01-13T10:29:28.102200Z

If you need the value afterwards then bind it in a let and check the value in a regular if.

popeye 2021-01-13T10:30:06.102400Z

yeah, I tried that first .. but wanted to reduce code so done for if-let

bnstvn 2021-01-13T14:33:08.104400Z

is there a comfortable way of inspecting interim values inside threading macros? i mean something better than (-&gt;&gt; 2 (#(doto % tap&gt; )) (* 3))

alexmiller 2021-01-13T14:33:56.104700Z

we're going to add one in 1.11 :) tap-&gt; :)

πŸ’― 5
bnstvn 2021-01-13T14:35:35.104900Z

thanks! that was fast! πŸ™‚

2021-01-13T14:40:30.106700Z

Cool! I normally just write a little debug function.

(defn debug [x](prn x) x)

(-&gt;&gt; foo
    (debug)
    (bar))

πŸ‘ 1
roelof 2021-01-13T15:14:27.107500Z

hmm,

roelof 2021-01-13T15:15:23.108500Z

Why is the healing-potion not updated right

(def warrior (ref {:hitpoint 15}))

(def healer (ref {:healing-potion 50}))


(defn heal [warrior healer]
   (dosync 
      (alter warrior update-in [:hitpoint] (partial + 25))
      (alter healer update-in [:healing-potion] (partial - 25))
  (get-in [:hitpoint] @warrior))
  (get-in [:healing-potion] @healer)
    (println warrior)
          (println healer))

(heal warrior healer)
outcome :
#ref[{:status :ready, :val {:hitpoint 40}} 0x14060ef1]
#ref[{:status :ready, :val {:healing-potion -25}} 0x2953a377]
nil

2021-01-13T15:18:00.109Z

are you doing (- 25 50), so 25 - 50 = -25? when you want (- 50 25) 50-25 = 25?

roelof 2021-01-13T15:20:45.110800Z

maybe ?

2021-01-13T15:21:16.111400Z

so if you chang ethe (partial - 25) to (partial - 13) you will get result of -37

2021-01-13T15:21:57.112500Z

(partial - 25) is returning another function, which the 50 is passed to, so its doing (- 25 50), I think.

2021-01-13T15:22:22.113Z

Instead of using partial, you might want something like (fn [cur] (- cur 25)), or an abbreviated form that Clojure allows for functions: #(- % 25)

πŸ‘ 1
2021-01-13T15:23:05.113400Z

It doesnt matter for + as 5+6 = 6+5, but 5-6!=6-5

roelof 2021-01-13T15:24:12.113900Z

@andy.fingerhut does not make a difference

roelof 2021-01-13T15:24:36.114400Z

#ref[{:status :ready, :val {:hitpoint 40}} 0x278238d9]
#ref[{:status :ready, :val {:healing-potion -25}} 0x40bd1764]
nil
code :
(def warrior (ref {:hitpoint 15}))

(def healer (ref {:healing-potion 50}))


(defn heal [warrior healer]
  (dosync
   (alter warrior update-in [:hitpoint] (partial + 25))
   (alter healer update-in [:healing-potion] #(- % 25)))
  (get-in [:hitpoint] @warrior))
(get-in [:healing-potion] @healer)
(println @warrior)
(println @healer)

(heal warrior healer)

2021-01-13T15:25:18.114900Z

@andy.fingerhut solution works for me.

(defn heal [warrior healer]
  (dosync
    (alter warrior update-in [:hitpoint] (partial + 25))
    (alter healer update-in [:healing-potion] #(- % 25))
    (get-in [:hitpoint] @warrior))
  (get-in [:healing-potion] @healer)
  (println warrior)
  (println healer))

=&gt; #'stuartstein777.aoc/heal
(def warrior (ref {:hitpoint 15}))

=&gt; #'stuartstein777.aoc/warrior
(def healer (ref {:healing-potion 50}))

=&gt; #'stuartstein777.aoc/healer
(heal warrior healer)

#object[clojure.lang.Ref 0xce48ab9 {:status :ready, :val {:hitpoint 40}}]
#object[clojure.lang.Ref 0x641e467b {:status :ready, :val {:healing-potion 25}}]

2021-01-13T15:25:30.115200Z

Did you resend the function to the repl after changing it?

roelof 2021-01-13T15:26:18.115500Z

yep

roelof 2021-01-13T15:26:29.115800Z

maybe a parentheses wrong

roelof 2021-01-13T15:26:33.116Z

Now it works

roelof 2021-01-13T15:26:50.116300Z

second question

roelof 2021-01-13T15:27:14.116800Z

how I can I now print the warrior and the healer instead of refs

2021-01-13T15:28:19.117700Z

For most kind of "mutable containers" in Clojure, including refs, atoms, and agents, you can use (deref x) to get the value "inside".

2021-01-13T15:28:23.118Z

deref it? (println (deref warrior))

2021-01-13T15:28:31.118200Z

The syntax @x is a shorthand for (deref x)

roelof 2021-01-13T15:29:15.118600Z

yep, found that out

roelof 2021-01-13T15:29:46.119400Z

last part of the puzzle . healing points cannot be more then 40

roelof 2021-01-13T15:29:56.119800Z

and the potion cannot be less then zero

2021-01-13T15:30:33.120500Z

Inside of the dosync, you can have conditional code like if or cond that checks whatever conditions you want, before it makes any calls to alter

roelof 2021-01-13T15:30:40.120700Z

maybe use let-if ??

2021-01-13T15:32:18.121Z

do you mean that even though healing potion is 50, you only want to use 40 of those points?

2021-01-13T15:34:36.122700Z

Minor comment: In your code pasted above, the expression (get-in [:hitpoint] @warrior) does get the new value from the warrior ref, and I am pretty sure that value is returned from the dosync expression, but there is nothing that can ever "see" or receive that return value, so it is redundant and unnecessary. The same applies to the following expression (get-in [:healing-potion] @healer)

2021-01-13T15:34:51.123100Z

The code will do the same thing, slightly faster, if those two expressions are removed from the function heal

2021-01-13T15:35:57.124Z

I mention it not so much because of the performance issue, but for learning purposes it is good to know that such expressions are almost always useless - they are pure functions of their input with no side effects, and their return value is ignored.

dpsutton 2021-01-13T15:39:30.124300Z

also, the arguments appear to be backwards

2021-01-13T15:40:34.124800Z

Doh! I should have noticed that, too. I guess it is a good thing those expressions are redundant, then πŸ™‚

roelof 2021-01-13T15:55:14.125400Z

hmm, why here a nullpointer exception

(defn hitpoint-validator[{:keys [hitpoint]}]
  (and (&lt;= hitpoint 40)))

(defn potion-validator [{:keys [potion]}]
  (and (&gt;= potion 0)))

(def warrior (ref {:hitpoint 15}
                  :validator hitpoint-validator))

(def healer (ref {:healing-potion 50}
                 :validator potion-validator))

roelof 2021-01-13T15:55:33.125900Z

I get that when ttrying the healer

dpsutton 2021-01-13T15:58:21.126100Z

potion versus healing-potion

samoleary 2021-01-13T15:58:23.126200Z

You're destructuring a different key in potion-validator to what you have in healer, :healing-potion vs potion

roelof 2021-01-13T15:59:33.126500Z

thanks, it works

roelof 2021-01-13T16:00:04.127200Z

the only thing I do not like is that it chrashes on a illigal state

roelof 2021-01-13T16:02:03.127500Z

(defn hitpoint-validator [{:keys [hitpoint]}]
  (&lt;= hitpoint 40))

(defn potion-validator [{:keys [healing-potion]}]
  (&gt;= healing-potion 0))

(def warrior (ref {:hitpoint 15}
                  :validator hitpoint-validator))

(def healer (ref {:healing-potion 50}
                 :validator potion-validator))


(defn heal [warrior healer]
  (dosync
   (alter warrior update-in [:hitpoint] (partial + 25))
   (alter healer update-in [:healing-potion] #(- % 25)))
  (get-in [:hitpoint] @warrior))
(get-in [:healing-potion] @healer)
(println "warrior:" @warrior)
(println "healer: " @healer)

(heal warrior healer)

roelof 2021-01-13T16:36:04.127600Z

thanks, that was it

Antonio Bibiano 2021-01-13T17:23:21.129600Z

I'm trying to parse a json using clojure.data.json but it wasn't in my project dependencies when I started the repl

Antonio Bibiano 2021-01-13T17:23:33.129900Z

there is no way to include it on the fly?

Antonio Bibiano 2021-01-13T17:25:01.130200Z

i'm currently using leiningen and calva

roelof 2021-01-13T17:26:45.130800Z

I thought you can do this : `

(require '[clojure.data.json :as json])
@antbbn

roelof 2021-01-13T17:27:00.131200Z

in the repl

popeye 2021-01-13T17:27:38.132200Z

I would like to understand destructing in clojure, Anyone bookmarked helpful website?

dpsutton 2021-01-13T17:27:41.132300Z

you need to restart the repl after adding it to your project dependencies

dpsutton 2021-01-13T17:29:00.134100Z

(there are ways to accomplish this but they depend on tooling and are beyond the scope of #beginners imo)

nnichols 2021-01-13T17:29:51.134200Z

There is a good guide here: https://clojure.org/guides/destructuring

Antonio Bibiano 2021-01-13T17:36:54.134500Z

got-it

popeye 2021-01-13T17:46:10.134800Z

Thanks πŸ™‚

Antonio Bibiano 2021-01-13T18:16:15.135200Z

i still get this

Antonio Bibiano 2021-01-13T18:16:21.135500Z

=&gt; clojure.data.json
Syntax error (ClassNotFoundException) compiling at (form-init18181492875778679100.clj:1:1629).
clojure.data.json

Antonio Bibiano 2021-01-13T18:17:08.136300Z

i put these in my :dependencies

Antonio Bibiano 2021-01-13T18:17:13.136700Z

:dependencies [[org.clojure/clojure "1.10.0"]
                 [org.clojure/data.json "1.0.0"]]

2021-01-13T18:17:16.136900Z

clojure.data.json is the name of a namespace, it is not a name bound to a value, so it has no value evaluated at the repl

2021-01-13T18:17:49.137200Z

have you looked at https://github.com/clojure/data.json#usage ?

Antonio Bibiano 2021-01-13T18:19:00.137700Z

you're right, i also tired this

=&gt; (clojure.data.json/read-str "{}")
Execution error (ClassNotFoundException) at java.net.URLClassLoader/findClass (URLClassLoader.java:471).
clojure.data.json

2021-01-13T18:19:22.138Z

did you try the first example code block in the usage section?

Antonio Bibiano 2021-01-13T18:20:29.138600Z

oh yeah :man-facepalming: i did not require it

Antonio Bibiano 2021-01-13T18:21:44.139Z

thanks for insisting πŸ˜„

roelof 2021-01-13T18:28:23.139700Z

(ns brave-book.chapter11
  (:require [clojure.core.async
             :as a
             :refer [&gt;! &lt;! &gt;!! &lt;!! go chan buffer close! thread
                     alts! alts!! timeout]]))
error:
:as - failed: #{:only} at: [:only :op :quoted-spec :spec]
:as - failed: #{:rename} at: [:rename :op :quoted-spec :spec]
(quote :as) - failed: #{:exclude} at: [:exclude :op :spec]
(quote :as) - failed: #{:only} at: [:only :op :spec]
(quote :as) - failed: #{:rename} at: [:rename :op :spec]

dpsutton 2021-01-13T18:33:53.139800Z

works for me

/tmp ❯❯❯ clj -A:async
Clojure 1.10.1
(ns brave-book.chapter11
  (:require [clojure.core.async
             :as a
             :refer [&gt;! &lt;! &gt;!! &lt;!! go chan buffer close! thread
                     alts! alts!! timeout]]))
nil
brave-book.chapter11=&gt;

roelof 2021-01-13T18:34:40.140Z

very wierd

roelof 2021-01-13T18:34:55.140200Z

with a ;lein project I do not seem to work

roelof 2021-01-13T18:35:21.140400Z

or there is somewhere else a problem

(ns brave-book.chapter11
  (:require [clojure.core.async
             :as a
             :refer [&gt;! &lt;! &gt;!! &lt;!! go chan buffer close! thread
                     alts! alts!! timeout]]))

(defn hot-dog-machine
  []
  (let [in (chan)
        out (chan)]
    (go (&lt;! in)
        (&gt;! out "hot dog"))
    [in out]))

(let [[in out] (hot-dog-machine)]
  (&gt;!! in "pocket lint")
  (&lt;!! out))

roelof 2021-01-13T18:36:15.140600Z

nope, only with the ns I see that problem

2021-01-13T18:36:44.140800Z

you should check the version of core.async you are using

dpsutton 2021-01-13T18:36:58.141100Z

/t/foobar ❯❯❯ lein repl
OpenJDK 64-Bit Server VM warning: Options -Xverify:none and -noverify were deprecated in JDK 13 and will likely be removed in a future release.
nREPL server started on port 51406 on host 127.0.0.1 - <nrepl://127.0.0.1:51406>
REPL-y 0.4.4, nREPL 0.7.0
Clojure 1.10.1
OpenJDK 64-Bit Server VM 13.0.2+8
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

foobar.core=&gt; (ns brave-book.chapter11
         #_=&gt;   (:require [clojure.core.async
         #_=&gt;              :as a
         #_=&gt;              :refer [&gt;! &lt;! &gt;!! &lt;!! go chan buffer close! thread
         #_=&gt;                      alts! alts!! timeout]]))
nil
brave-book.chapter11=&gt;

2021-01-13T18:37:07.141300Z

and you haven't reported the full error

dpsutton 2021-01-13T18:37:11.141500Z

i suspect the file isn't saved and you are evaluating something else

2021-01-13T18:37:13.141700Z

just part of the spec failure

roelof 2021-01-13T18:38:55.141900Z

I have save it and the brave book uses these versions :

:dependencies [[org.clojure/clojure "1.10.0"]
                 [org.clojure/core.async "0.1.346.0-17112a-alpha"]]

roelof 2021-01-13T18:40:26.142100Z

oke, with a newer version it seems to work

2021-01-13T18:43:44.142300Z

newer versions of clojure are stricter about what they accept in ns forms, and some older versions of core.async have wonky ns forms

2021-01-13T18:44:42.142500Z

the full error instead of just the excerpt would have had some indication of which ns form was causing the problem, which likely would have shown it to be something in the core.async library, not in your code

roelof 2021-01-13T18:46:56.142700Z

oke, I thought I had paste the full error

Ovidiu Stoica 2021-01-13T19:05:33.144500Z

Hello, I am doing the Learn Reitit Course and working with next.jdbc , after connecting my DB with heroku, when I am trying to run my first sql command I get this error

Ovidiu Stoica 2021-01-13T19:05:54.144600Z

Ovidiu Stoica 2021-01-13T19:06:02.145100Z

The jdbc/execute part

Ovidiu Stoica 2021-01-13T19:06:50.145600Z

Here is the user.clj file, I will provide more if needed

(ns user
  (:require [integrant.repl :as ig-repl]
            [integrant.core :as ig]
            [integrant.repl.state :as state]
            [cheffy.server]
            [next.jdbc :as jdbc]))

(ig-repl/set-prep!
  (fn [] (-&gt; "resources/config.edn" slurp ig/read-string)))

(def go ig-repl/go)
(def halt ig-repl/halt)
(def reset ig-repl/reset)
(def reset-all ig-repl/reset-all)

(def app (-&gt; state/system :cheffy/app))
(def db (-&gt; state/system :db/postgres))

(comment
  (app {:request-method :get
        :uri            "/swagger.json"})
  (jdbc/execute! db ["SELECT * FROM recipe"])
  (go)
  (halt)
  (reset))

Ovidiu Stoica 2021-01-13T19:23:55.146Z

Nevermind, I fixed it. But Don’t ask me how xD

seancorfield 2021-01-13T19:45:21.146800Z

@ovidiu.stoica1094 Looks like you may have evaluated that code before db was initialized.

roelof 2021-01-13T20:18:00.147300Z

is there a way I do not have to explicit return nil :

(defn distance [strand1 strand2] 
  (if (= (count strand1) (count strand2))
    (-&gt;&gt;  (map vector strand1 strand2)
          (map (partial reduce not=))
          (filter true?)
          (count))
    nil))

Henri Schmidt 2021-01-13T20:19:45.147600Z

@roelof simply remove the else clause

Henri Schmidt 2021-01-13T20:20:10.148Z

by default (if p x) returns nill if p is not satisfied

2021-01-13T20:20:41.148500Z

Or use when

roelof 2021-01-13T20:20:51.148800Z

oke, but then I see a message that the else is missing

roelof 2021-01-13T20:21:07.149600Z

@hiredman that seems to work Thanks

2021-01-13T20:21:32.150400Z

That message isn't from clojure, that message is from some extra tool you are using

πŸ‘ 1
2021-01-13T20:21:44.151200Z

A linter or something

Henri Schmidt 2021-01-13T20:21:51.151400Z

Yes @hiredman is correct.

Henri Schmidt 2021-01-13T20:22:21.152100Z

I'd prefer if over when in this case because you aren't performing sequential logic and don't need to evaluate the body in an implicit do block.

2021-01-13T20:24:07.153800Z

if vs. when was the one topic that caused the last dev team I was on to errupt into flames

bronsa 2021-01-13T20:24:52.154600Z

odd hill to die on

Henri Schmidt 2021-01-13T20:24:53.154800Z

@hiredman what was the conclusion?

2021-01-13T20:25:06.155200Z

There are takes on this topic, and debates that go back to common lisp

2021-01-13T20:25:22.155700Z

There was no conclusion, only conflict

πŸ˜‚ 1
Henri Schmidt 2021-01-13T20:25:40.156200Z

Interesting, I have never heard of this debate before...

2021-01-13T20:26:01.157Z

I think it is interesting to hear the debate at most once πŸ™‚

Henri Schmidt 2021-01-13T20:26:28.158Z

Is the debate on the other side an argument that the implicit do block avoids accidental else clauses?

dpsutton 2021-01-13T20:27:10.159100Z

the version i've heard is that when is only for side effects. and otherwise use if even if it only has a single branch. I don't understand the logic of it

2021-01-13T20:27:30.159300Z

I believe one side of the debate argues that when does (or should) imply that the expression is there for side effects, but if does not imply that.

Henri Schmidt 2021-01-13T20:27:44.159700Z

That seems to be my reasoning as welll...

2021-01-13T20:28:30.160600Z

Nothing in the language behavior itself requires or implies either side of the debate, that I know of. It is a matter of style that some developers feel very strongly about. Not miles away from a tabs vs. spaces debate.

2021-01-13T20:32:10.162100Z

But by that logic, fn forms should only be used for side effects, and cond forms should never have side effects

2021-01-13T20:35:27.164100Z

The when implies side effects camp originates, I believe in common lisp, where an influential comp.lang.lisp poster took that position a long time ago

bronsa 2021-01-13T20:37:32.164500Z

to end this debate once and for all we should just have a when! macro for side-effects

πŸ˜› 1
2021-01-13T20:38:09.165700Z

I am trying to remember the guys name to see if I can dig up the post

dpsutton 2021-01-13T20:38:12.165900Z

but it will have a 1/1000 chance of not executing body even when the test is true

bronsa 2021-01-13T20:38:16.166100Z

within the macro, you can suffix every symbol with ! to signal side-effects and it will automatically work

seancorfield 2021-01-13T20:38:20.166400Z

I seem to recall technomancy being a very fierce advocate of if instead of when?

2021-01-13T20:38:29.166800Z

I think his last name started with an N

bronsa 2021-01-13T20:38:38.167Z

erik naggum?

2021-01-13T20:39:02.167200Z

That must be it

bronsa 2021-01-13T20:39:05.167400Z

oh boy

2021-01-13T20:39:36.168Z

No, no. Don't end the debate. Simply try to find a way that it wastes more times for your competitors/opponents than it does for you πŸ™‚

bronsa 2021-01-13T20:40:48.168100Z

that'd be when?!?

dpsutton 2021-01-13T20:41:08.168300Z

lol

2021-01-13T20:46:00.169700Z

technomancy was a member of the team I mentioned

seancorfield 2021-01-13T20:46:42.170400Z

I seem to recall quite a heated discussion on IRC around that time (2012) πŸ™‚

2021-01-13T21:00:55.171800Z

To me "when" for side effect ONLY seem to not make any sense in a language that doesn't have void returns

2021-01-13T21:02:44.175800Z

I think rich at some point posted something to the Google group saying when is idiomatic for single branch conditionals, but some felt he was wrong or that his wording was ambiguous in someway (could have been, I don't really recall)

2021-01-13T21:04:04.176400Z

An annoying this is googling if and when is basically impossible

roelof 2021-01-13T21:05:35.176900Z

oops, sorry for my question

2021-01-13T21:06:07.177300Z

The debate was there before your question, and will remain after your question πŸ™‚

roelof 2021-01-13T21:07:03.178400Z

o, I thought this discussion was about if versus when and I started a question how to do things better without explicit return nil

alexmiller 2021-01-13T21:07:20.178700Z

I don't remember if Rich has said that or not, but I've said it and that's my opinion

2021-01-13T21:08:01.178900Z

The debate is over 20 years old, I think.

2021-01-13T21:08:14.179100Z

starting before Clojure existed.

😁 1
alexmiller 2021-01-13T21:08:32.179400Z

but you know, whatever

roelof 2021-01-13T21:09:06.179700Z

oke

roelof 2021-01-13T21:09:23.179900Z

looks I started it again with my question

2021-01-13T21:09:34.180100Z

This is a mild reasonable one, though πŸ™‚

roelof 2021-01-13T21:10:13.180300Z

oke

caumond 2021-01-13T21:19:06.183700Z

Not sure to follow you guys. At the end both forms are executing properly ? So its only a style issue. Am I right ? The 1/ 1000 sentence of @dpsutton and other comments make me doubting

2021-01-13T21:24:45.184Z

yes, it is a style issue

dpsutton 2021-01-13T21:34:05.184600Z

sorry. the 1/1000 randomness was just a joke πŸ™‚

caumond 2021-01-13T21:37:16.186200Z

No pb, my not so good english and clojure doubts are sometime an awful mix

2021-01-13T21:38:59.186300Z

I'll bite! I feel like you can't enforce semantics through conventions. So having when only for side effect is a "good intention", but in practice, it's not an enforceable semantic. So realistically you can't rely on it to guarantee you anything. Thus it seems futile to assume when is only for side effect, and a lost cause to nag everyone who "mistakenly" uses when for returning a useful nil instead of only for side effects.

dpsutton 2021-01-13T21:42:06.186900Z

well i'm glad you asked. doubt anyone ever minds being asked for clarification.

2021-01-13T21:51:02.187300Z

That means, even before debating the "usefulness and merits" of a construct that would "execute a side-effect if condition is true", I can assert that trying to make when behave with those semantics by pure convention is an exercise in futility.

seancorfield 2021-01-13T21:56:21.187600Z

I always wondered what he would think about situations where you have "if some-condition then execute-side-effects else execute-other-side-effects"? Would he insist on "when some-condition execute-side-effects; when not some-condition execute-other-side-effects"?

πŸ˜€ 1
2021-01-13T21:57:13.187800Z

when just isn't that construct, when returns the last result of expressions if cond, else nil. That's what it is, seems hopeless to pretend its something else by using it exclusively for a subset of what it can be used for. And I can see people trying to do that actually getting burned out trying, cause "Good Intentions" don't work to enforce things. They can reduce the occurrence of it, but never prevent it. So if you're not okay with anything but a full guarantee, you got to find a different approach. For example, you could make a new construct that always returns nil, but if cond it also runs some side-effect. Such a construct could only be used for side-effect (or as a sub for nil I guess). If you even wanted to prevent someone using it as a sub for nil, you could have it return an exception maybe, or a thing that when deref throws an execption: Not allowed to use return of ...., ..... is only to be used for side-effects. Something like that.

seancorfield 2021-01-13T21:57:55.188Z

(although maybe he's really only railing against (when some-condition some-return-value) and preferring (if some-condition some-return-value nil) except with an implicit nil? I find it a hard position to justify)

2021-01-13T21:59:11.188200Z

That's what I thought the idea was. I'm not sure if its when only for side effect, and if only for pure logic, or only the former.

2021-01-13T22:00:16.188400Z

I think the idea was that at a glance, if you see when, you know some side effect is happening in that piece of code. (and if you use when for pure behavior, that messes up with this "at a glance" reading)

2021-01-13T22:00:58.188600Z

So in order to be able to trust your "at a glance" reading of the code, you need people to only use when for side effects. Which, like I said, I think is an exercise in futility πŸ˜›

2021-01-13T22:04:34.188800Z

(defmacro maybe-do
  [condition &amp; forms]
  `(do (when ~condition ~@forms)
     (delay (throw (ex-info "Can't read from void return!" {:type :void-return-error})))))
Something like this would be better. Now I don't know if I'm convinced in how useful it is to use this to indicate "hey I'm doing side effect here, nothing else!!", but at least you can be sure that when someone uses that, its only doing side effect.

2021-01-13T22:09:25.190Z

Hiredman said that he thought Rick said: > when is idiomatic for single branch conditionals So I assume this is what Alex is referring too. So it be that when your condition has a single branch, use when, otherwise use if or cond.

seancorfield 2021-01-13T22:10:54.190600Z

Ah, that "it", not the other "it" πŸ™‚

seancorfield 2021-01-13T22:11:36.191400Z

(just that a lot of the earlier discussion focused on Phil Hagelberg's position which is the opposite of that)

2021-01-13T22:12:49.192700Z

I think the more common use in Clojure is: * When you have 1 branch use when * When you have 2 branch use if * When you have 3 or more branch use cond * When you want to choose between "one of many value" use case * If you need to do more than one thing per branch wrap it in a do unless you're using when, then you don't have to cause when is unambiguous so do would be redundant. * Side effect or not, it doesn't matter

6
πŸ™Œ 1
alexmiller 2021-01-13T22:15:07.193Z

correct - when for single branch conditionals is my preference

seancorfield 2021-01-13T22:16:52.193200Z

Thank you! I checked the (community) Clojure Style Guide and it doesn't seem to have an opinion on this particular issue. Which surprised me a bit.