Fork me on GitHub
#clojure
<
2021-05-13
>
Fabim10:05:50

Hi, I’m often want to add an optional values to a hash-map only if its not nil: (conj {:a 1} (when (:x m) {:x (:x m)})) Is there a more elegant way do do this?

jjttjj11:05:46

(cond-> {:a 1}
  (:x m) (assoc :x (:x m)))

👍 3
Ed11:05:24

bear in mind this case

(let [m {:x false}]
    (cond-> {:a 1}
      (:x m) (assoc :x (:x m))))
which I know is not covered by the original code, but is what you actually said, but you might want to consider this
(let [m {:x false}]
    (cond-> {:a 1}
      (some? (:x m)) (assoc :x (:x m))))
or
(let [m {:x false}]
    (merge {:a 1}
           (select-keys m [:x])))

Fabim11:05:20

I read that merge and select-keys are expensive (to use for just one key)

Ed11:05:55

depends what you mean by expensive ... sure, it's a few more function calls than a straight conj but it's not like calling a database or shelling out and it's not going to incur a huge gc pause or anything 😉

Ed11:05:42

if you think it might be slow, measure it for your use case 😉

wotbrew11:05:10

I just write a dissoc-nils helper for this case because I like using the literal syntax when building a larger map and the cond-> assoc certainly gets cumbersome if you have a lot of 'maybe' keys.

zendevil.eth17:05:46

How do I put the Authorization header in an clj-http post request?

fetch('', {
  method: 'GET',
  headers: {
    'Authorization' : `Bearer ${API_KEY}`
  }
})

p-himik17:05:38

Have you looked in the documentation?

zendevil.eth17:05:25

I didn’t find a way to add a header

zendevil.eth17:05:14

I’m sending this request:

(post "" {:headers {"Authorization" "Bearer DT56EIQ3irkCLvAAYTzGfgtt"}                            :form-params {:template "j14WwV5VkY4Da7XrBx"                                              :modifications                                                           [                                                            {                                      :name "text_container_0"                              :text "You can change this text"                                                    :color nil                                               :background nil                                                             }                                                            {                                              :name "circle_1"                                                    :color nil                                                             }                                                            {                                         :name "star_rating_2"                                                              :rating 70                                                          }                                                ]                                                            :webhook_url nil                                                           :transparent false                                                            :metadata nil                                          }})
But getting: “{\“message\“:\“Invalid parameter: modifications objects is empty or not an array\“}”

lukasz17:05:38

Is the API you're working with use form params or JSON body? From the error message you're getting your problem is not auth, but the fact that you're not json-encoding the request body

zendevil.eth17:05:27

This:

(post "" {:headers {"Authorization" "Bearer DT56EIQ3irkCLvAAYTzGfgtt"}
                                              :body (cheshire/generate-string
                                                     {:template "j14WwV5VkY4Da7XrBx"
                                                      :modifications
                                                      [
                                                       {
                                                        :name "text_container_0"
                                                        :text "You can change this text"
                                                        :color nil
                                                        :background nil
                                                        }
                                                       {
                                                        :name "circle_1"
                                                        :color nil
                                                        }
                                                       {
                                                        :name "star_rating_2"
                                                        :rating 70
                                                        }
                                                       ]
                                                      :webhook_url nil
                                                      :transparent false
                                                      :metadata nil
                                                      })})
gives:

zendevil.eth17:05:11

“{\“message\“:\“Required parameter: template\“}”

lukasz18:05:08

I'm not familiar with the API you're using - the shape of the data is probably not right

zendevil.eth18:05:14

The supposed body for this particular request:

{
  "template": "j14WwV5VkY4Da7XrBx",
  "modifications": [
    {
      "name": "text_container_0",
      "text": "You can change this text",
      "color": null,
      "background": null
    },
    {
      "name": "circle_1",
      "color": null
    },
    {
      "name": "star_rating_2",
      "rating": 70
    }
  ],
  "webhook_url": null,
  "transparent": false,
  "metadata": null
}

seancorfield18:05:04

@U01F1TM2FD5 It may require "Content-Type" "application/json" in the headers as well, in order to trigger JSON handling on their end.

seancorfield18:05:19

(I’m just basing that on what’s in their docs — which I’d never seen before 🙂 )

Kenneth Cheung17:05:16

I'm working on performance optimizations to use unboxed arithmetic by using primitive type hints. I'm not getting the speed improvement I was hoping for. In inspecting the java code that is translated by Clojure, it seems the efficient version of the function isn't being called. Here's an example

(defn my-add
  [^double a ^double b]
  (+ 1.0 a b))
Using clj-java-decompiler.core/decompile
(decompile (defn my-add
             [^double a ^double b]
             (+ 1.0 a b)))
=>
// Decompiling class: user$my_add
import clojure.lang.*;

public final class user$my_add extends AFunction implements DDO
{
    public static Object invokeStatic(final double a, final double y) {
        return Numbers.add(1.0 + a, y);
    }
    
    @Override
    public Object invoke(final Object o, final Object o2) {
        return invokeStatic(RT.doubleCast(o), RT.doubleCast(o2));
    }
    
    @Override
    public final Object invokePrim(final double a, final double n) {
        return invokeStatic(a, n);
    }
}
Three functions are created invokeStatic, invoke, and invokePrim. The efficient code being invokePrim and invokeStatic as these are properly typed. invoke on the other hand incurs some runtime penalty with the RT.longCast Now let's look at the decompile of using this function
(decompile (my-add 2.0 3.0))

// Decompiling class: cjd__init
import clojure.lang.*;
public class cjd__init
{
    public static final Var const__0;
    public static final Object const__1;
    public static final Object const__2;
    public static void load() {
        ((IFn)cjd__init.const__0.getRawRoot()).invoke(cjd__init.const__1, cjd__init.const__2); <-- Notice here we are calling invoke not invokeStatic
    }
    public static void __init0() {
        const__0 = RT.var("user", "my-add");
        const__1 = 2.0;
        const__2 = 3.0;
    }
    static {
        __init0();
        Compiler.pushNSandLoader(RT.classForName("cjd__init").getClassLoader());
        try {
            load();
            Var.popThreadBindings();
        }
        finally {
            Var.popThreadBindings();
        }
    }
}
I'm noticing that in the load fn invoke is actually being called, and not invokeStatic or invokPrim . Is there a way to make sure I use invokStatic or invokePrim ? Thank you if you have gotten this far to my long post.

p-himik17:05:28

Your first version of my-add uses double, the decompiled version uses long. If you're using the latter, I imagine that's the reason, because 2.0 and 3.0 are not longs.

Kenneth Cheung17:05:05

yes sorry, fixed it. Pasted the wrong output from my repl

Alex Miller (Clojure team)17:05:30

I would look dubiously at decompile too. It is better to look directly at the generated bytecode

👍 3
jumar17:05:21

Alex has an interesting blog post about these things here https://insideclojure.org/2014/12/15/warn-on-boxed/ 🙂

hiredman17:05:19

I would try looking at the decompiled output of (fn [] (my-add 2.0 3.0)) instead of the expression directly

jumar18:05:05

Yeah, what hiredman mentioned is interesting:

(defn my-add-long
  [^long a ^long b]
  (+ 1 a b))

(decompile (fn [] (my-add-long 2 3)))
;;=> 
// Decompiling class: clojure_experiments/performance/performance$fn__28976
package clojure_experiments.performance;

import clojure.lang.*;

public final class performance$fn__28976 extends AFunction
{
    public static final Var const__0;
    
    public static Object invokeStatic() {
        return ((LLO)performance$fn__28976.const__0.getRawRoot()).invokePrim(2L, 3L);
    }
    
    @Override
    public Object invoke() {
        return invokeStatic();
    }
    
    static {
        const__0 = RT.var("clojure-experiments.performance.performance", "my-add-long");
    }
}

Kenneth Cheung18:05:27

interesting! Does this mean I should wrap all my calls in an anonymous function?

hiredman18:05:19

it means function calls compiled inside a function are more representative of how function calls are compiled then top level function calls in namespaces

Kenneth Cheung18:05:46

oh I see. Thank you for this clarification!

Kenneth Cheung18:05:28

@hiredman oh! Looks like this indeed calls invokeStatic

// Decompiling class: user$fn__7239
import clojure.lang.*;

public final class user$fn__7239 extends AFunction
{
    public static final Var const__0;
    
    public static Object invokeStatic() {
        return ((DDO)user$fn__7239.const__0.getRawRoot()).invokePrim(2.0, 3.0);
    }
    
    @Override
    public Object invoke() {
        return invokeStatic();
    }
    
    static {
        const__0 = RT.var("user", "my-add");
    }
}

Kenneth Cheung18:05:44

@alexmiller Looking at the byte code. Indeed it calls invoke.Prim . Thanks

(ns scratch.primitive)

(defn my-add
  [^double a ^double b]
  (+ 1.0 a b))

(defn test-fn []
  (my-add 2.0 3.0))

Byte Code
public final class scratch.primitive$test_fn extends clojure.lang.AFunction {
  public static final clojure.lang.Var const__0;
    descriptor: Lclojure/lang/Var;

  public scratch.primitive$test_fn();
    descriptor: ()V
    Code:
       0: aload_0
       1: invokespecial #9                  // Method clojure/lang/AFunction."<init>":()V
       4: return
    LineNumberTable:
      line 7: 0

  public static java.lang.Object invokeStatic();
    descriptor: ()Ljava/lang/Object;
    Code:
       0: getstatic     #15                 // Field const__0:Lclojure/lang/Var;
       3: invokevirtual #20                 // Method clojure/lang/Var.getRawRoot:()Ljava/lang/Object;
       6: checkcast     #22                 // class clojure/lang/IFn$DDO
       9: ldc2_w        #23                 // double 2.0d
      12: ldc2_w        #25                 // double 3.0d
      15: invokeinterface #30,  5           // InterfaceMethod clojure/lang/IFn$DDO.invokePrim:(DD)Ljava/lang/Object;
      20: areturn
    LineNumberTable:
      line 7: 0
      line 8: 15

  public java.lang.Object invoke();
    descriptor: ()Ljava/lang/Object;
    Code:
       0: invokestatic  #33                 // Method invokeStatic:()Ljava/lang/Object;
       3: areturn
    LineNumberTable:
      line 7: 0

  public static {};
    descriptor: ()V
    Code:
       0: ldc           #36                 // String scratch.primitive
       2: ldc           #38                 // String my-add
       4: invokestatic  #44                 // Method clojure/lang/RT.var:(Ljava/lang/String;Ljava/lang/String;)Lclojure/lang/Var;
       7: checkcast     #17                 // class clojure/lang/Var
      10: putstatic     #15                 // Field const__0:Lclojure/lang/Var;
      13: return
    LineNumberTable:
      line 7: 0
}

lilactown21:05:00

I remember Clojure usage stats being bandied about every now and then. anyone know where I can find those?

seancorfield22:05:13

What do you mean by “usage stats”? Are you thinking of the State of Clojure survey/results?

seancorfield22:05:22

If so, that’s on http://clojure.org under news.

lilactown04:05:20

no, like monthly downloads or something

seancorfield04:05:02

They're a bit meaningless, because you don't really "download" Clojure, you depend on it and it's up to individual Maven-like installations whether they need to download it or not.

seancorfield04:05:50

If your local repo has a copy, you don't need to download it. If you're starting fresh every time, you'll download it every time.

flowthing04:05:31

Probably even less meaningful than Maven stats, but I've seen this link around multiple times when the topic of Clojure has come up in another forum (http://lobste.rs, HN, company chat, etc.): https://trends.google.com/trends/explore?date=all&amp;q=%2Fm%2F03yb8hb

flowthing04:05:45

Apart from that, there's this regarding Maven download stats (from a year ago): https://pay.reddit.com/r/Clojure/comments/eakyfu/is_clojure_on_the_decline/favzns1/

lilactown14:05:35

great, thanks!