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?
(cond-> {:a 1}
(:x m) (assoc :x (:x m)))
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])))
I read that merge and select-keys are expensive (to use for just one key)
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 😉
if you think it might be slow, measure it for your use case 😉
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.
How do I put the Authorization header in an clj-http post request?
fetch('<https://api.bannerbear.com/v2/images>', {
method: 'GET',
headers: {
'Authorization' : `Bearer ${API_KEY}`
}
})
(post “https://api.bannerbear.com/v2/images” {???})
Have you looked in the documentation?
I didn’t find a way to add a header
thanks
I’m sending this request:
(post "<https://api.bannerbear.com/v2/images>" {: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\“}”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.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
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.
This:
(post "<https://api.bannerbear.com/v2/images>" {: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:yes sorry, fixed it. Pasted the wrong output from my repl
“{\“message\“:\“Required parameter: template\“}”
I would look dubiously at decompile too. It is better to look directly at the generated bytecode
Alex has an interesting blog post about these things here https://insideclojure.org/2014/12/15/warn-on-boxed/ 🙂
I would try looking at the decompiled output of (fn [] (my-add 2.0 3.0))
instead of the expression directly
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");
}
}
@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");
}
}
interesting! Does this mean I should wrap all my calls in an anonymous function?
no
I'm not familiar with the API you're using - the shape of the data is probably not right
it means function calls compiled inside a function are more representative of how function calls are compiled then top level function calls in namespaces
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
}
oh I see. Thank you for this clarification!
@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
}
@ps It may require "Content-Type" "application/json"
in the headers as well, in order to trigger JSON handling on their end.
(I’m just basing that on what’s in their docs — which I’d never seen before 🙂 )
I remember Clojure usage stats being bandied about every now and then. anyone know where I can find those?
great, thanks!
What do you mean by “usage stats”? Are you thinking of the State of Clojure survey/results?
If so, that’s on http://clojure.org under news.