Fork me on GitHub
#clojure-italy
<
2019-05-14
>
alan07:05:14

@andrea.imparato se vuoi evitare di fare 108 require mentre stai prototipando ricordati che tutto ciò che sta dentro Clojure + il classpath lo puoi usare con clojure.data/diff, io spesso per clojure.string faccio così e se alla fine l'ho usato solo 1-2 volte in tutto non sto a fare require

bronsa08:05:20

occhio, che questo vale solo per namespace che sono gia` stati require'd, che e` il caso per clojure.string (usato da clojure.repl) ma non per clojure.data

👍 3
alan07:05:10

A questo proposito, esiste un modo (con Emacs o altro) per far prendere a :require nella definizione del namespace tutti i singoli :refer in automatico? Cioè da (:require [clojure.string :as s]) a (:require [clojure.string :refer [(le funzioni che uso nel namespace)] :as s])?

alan09:05:41

Questo è uno di quei (pochi) casi in cui anche l'esatto contrario è più che accettabile 😄

skuro09:05:14

@justalanm se sposi la filosofia che i namespaced names (e.g. (str/join ...)) sono meglio dei :refer'ed ones (e.g. (join ...)), una volta installato cljr-refactor di norma appena scrivi lo slash in (str/) ti prova ad aggiornare la ns declaration (non estremamente preciso, comunque C-h f cljr-slash e C-h f cljr-magic-require-namespaces)

👍 1
Andrea Imparato09:05:52

buongiorno, io chiedo allora, è sempre buona pratica mettere il :refer nei require? è così brutto lasciarli senza?

alan09:05:49

@andrea.imparato Come dicevano anche gli altri il top sarebbe [org.library :as lib], seguito da singoli [org.library :refer [fn1 fn2]] e di solito si evita [org.library :refer :all]

alan09:05:26

Però mentre nella community Python le librerie più usate ormai hanno alias standard (pandas as pd, numpy as np, tensorflow as tf, etc) ho notato che in Clojure c'è molta più libertà e fantasia a riguardo

skuro09:05:55

io onestamente preferisco non usare mai :refer, usando il namespace di ogni symbol come visual hint di "WARNING: funzione esterna, AKA dependency, AKA robba architetturale"

reborg10:05:07

Io lo uso in maniera soggettiva (e limitata). 1. se il significato della funzione e’ ovvio 2. se mi servono solo 1 o 2 fns e 3. se aumenta la leggibilita’ del codice in una sezione dove e’ usata di frequente.

manuel10:05:59

praticamente pure io come i due messeri qui sopra. 🙂

skuro10:05:27

un esempio di funzioni per le quali uso il :refer nonostante la guideline di non usarli mai e' clojure.pprint/pprint: e' sufficientemente self-explanatory da non necessitare un namespace alias

skuro10:05:53

oppure nel classico dev.clj che e' in classpath solo in development, allora la' vado di libertinismo

reborg10:05:57

Giusto precisazion: pprint e’ auto nel REPL.

reborg10:05:16

So che e’ forzare un po’ la mano, ma il rompicapo di oggi e’:

user=> (def ^:const A (fn [_] A))
#'user/A
user=> (A 1)
#object[user$A 0x315f43d5 "[email protected]"]
user=> (def ^:const B (constantly B))
#'user/B
user=> (B 1)
IllegalArgumentException No matching ctor found for class clojure.core$constantly$fn__5394  clojure.lang.Reflector.invokeConstructor (Reflector.java:163)

bronsa10:05:07

^:const e closed overs non vanno d'accordo

bronsa10:05:39

alternativa che esplicita il problema

user=> (def ^:const c (let [c c] (fn [_] c)))
#'user/c
user=> (c 1)
Execution error (IllegalArgumentException) at user$eval146/<clinit> (REPL:1).
No matching ctor found for class user$fn__142$fn__143

reborg10:05:39

Devo ricordarmi di specificare Bronsa non puo’ rispondere :)

reborg10:05:03

Ma sono cosi’ intricati che bisogna sapere com’e’ implementato Clojure.

bronsa10:05:05

si` non credo questo rompicapo sia molto accessibile :)

Andrea Imparato12:05:47

necessito consiglio, come posso generate a partire da un vector, una stringa che poi posso usare per fare binding con una query sql? Mi spiego: (query! "select * from table where field in ($1)" parameters) parameters è un vector ovviamente Qualcuno di voi ha mai fatto qualcosa di simile?

manuel12:05:46

io di solito uso HugSQL o HoneySQL

Andrea Imparato12:05:36

ho provato honeysql

Andrea Imparato12:05:49

però non capisco perchè questa mappa non mi genera correttamente la query che voglio 😕

(def sqlmap {:select [:*]
             :from [:table]
             :where [:in :field [ "1" "2" ]]})

Andrea Imparato12:05:22

quindi dato che mi serve veramente fare solo 1 query volevo provare a generarmela “a mano”

manuel12:05:59

quindi vuoi concatenare stringhe per creare l'SQL che ti serve?

manuel12:05:21

se è così, non capisco a che serve $1

Andrea Imparato12:05:45

per farmi l’escaping delle stringhe?

manuel12:05:57

ah ok, quindi parameters è una collezione di stringhe escaped?

manuel12:05:08

o è query! che si occupa di tutto?

Andrea Imparato12:05:00

se faccio il binding il driver del db mi fa l’escaping, giusto?

Andrea Imparato12:05:08

(almeno penso io)

manuel12:05:29

il driver JDBC fa l'escape nel caso di preparedstatement

manuel12:05:04

la mappa che hai messo prima, con HoneySQL mi genera:

> (hc/format sqlmap)
;; => ["SELECT * FROM table WHERE (field in (?, ?))" "1" "2"]

Andrea Imparato12:05:03

che mi pare corretta infatti,ma quando la uso con https://github.com/alaisi/postgres.async mi sputa fuori un’eccezione 😕

manuel12:05:25

che eccezione?

Andrea Imparato12:05:41

Class: java.lang.String
Value: "#error {\n :cause \"ERROR: SQLSTATE=42601, MESSAGE=syntax error at or near \\\",\\\"\"\n :via\n [{:type com.github.pgasync.SqlException\n   :message \"ERROR: SQLSTATE=42601, MESSAGE=syntax error at or near \\\",\\\"\"\n   :at [com.github.pgasync.impl.netty.NettyPgProtocolStream toSqlException \"NettyPgProtocolStream.java\" 223]}]\n :trace\n [[com.github.pgasync.impl.netty.NettyPgProtocolStream toSqlException \"NettyPgProtocolStream.java\" 223]\n  [com.github.pgasync.impl.netty.NettyPgProtocolStream access$300 \"NettyPgProtocolStream.java\" 46]\n  [com.github.pgasync.impl.netty.NettyPgProtocolStream$1 onNext \"NettyPgProtocolStream.java\" 197]\n  [com.github.pgasync.impl.netty.NettyPgProtocolStream$5 channelRead \"NettyPgProtocolStream.java\" 304]\n  [io.netty.channel.AbstractChannelHandlerContext invokeChannelRead \"AbstractChannelHandlerContext.java\" 348]\n  [io.netty.channel.AbstractChannelHandlerContext invokeChannelRead \"AbstractChannelHandlerContext.java\" 334]\n  [io.netty.channel.AbstractChannelHandlerContext fireChannelRead \"AbstractChannelHandlerContext.java\" 326]\n  [io.netty.handler.codec.ByteToMessageDecoder fireChannelRead \"ByteToMessageDecoder.java\" 293]\n  [io.netty.handler.codec.ByteToMessageDecoder channelRead \"ByteToMessageDecoder.java\" 267]\n  [io.netty.channel.AbstractChannelHandlerContext invokeChannelRead \"AbstractChannelHandlerContext.java\" 348]\n  [io.netty.channel.AbstractChannelHandlerContext invokeChannelRead \"AbstractChannelHandlerContext.java\" 334]\n  [io.netty.channel.AbstractChannelHandlerContext fireChannelRead \"AbstractChannelHandlerContext.java\" 326]\n  [io.netty.handler.codec.ByteToMessageDecoder fireChannelRead \"ByteToMessageDecoder.java\" 293]\n  [io.netty.handler.codec.ByteToMessageDecoder fireChannelRead \"ByteToMessageDecoder.java\" 280]\n  [io.netty.handler.codec.ByteToMessageDecoder callDecode \"ByteToMessageDecoder.java\" 396]\n  [io.netty.handler.codec.ByteToMessageDecoder channelRead \"ByteToMessageDecoder.java\" 248]\n  [io.netty.channel.AbstractChannelHandlerContext invokeChannelRead \"AbstractChannelHandlerContext.java\" 348]\n  [io.netty.channel.AbstractChannelHandlerContext invokeChannelRead \"AbstractChannelHandlerContext.java\" 334]\n  [io.netty.channel.AbstractChannelHandlerContext fireChannelRead \"AbstractChannelHandlerContext.java\" 326]\n  [io.netty.channel.DefaultChannelPipeline$HeadContext channelRead \"DefaultChannelPipeline.java\" 1320]\n  [io.netty.channel.AbstractChannelHandlerContext invokeChannelRead \"AbstractChannelHandlerContext.java\" 348]\n  [io.netty.channel.AbstractChannelHandlerContext invokeChannelRead \"AbstractChannelHandlerContext.java\" 334]\n  [io.netty.channel.DefaultChannelPipeline fireChannelRead \"DefaultChannelPipeline.java\" 905]\n  [io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe read \"AbstractNioByteChannel.java\" 123]\n  [io.netty.channel.nio.NioEventLoop processSelectedKey \"NioEventLoop.java\" 563]\n  [io.netty.channel.nio.NioEventLoop processSelectedKeysOptimized \"NioEventLoop.java\" 504]\n  [io.netty.channel.nio.NioEventLoop processSelectedKeys \"NioEventLoop.java\" 418]\n  [io.netty.channel.nio.NioEventLoop run \"NioEventLoop.java\" 390]\n  [io.netty.util.concurrent.SingleThreadEventExecutor$5 run \"SingleThreadEventExecutor.java\" 742]\n  [io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator run \"DefaultThreadFactory.java\" 145]\n  [java.lang.Thread run \"Thread.java\" 748]]}"

manuel12:05:43

(tieni conto che non ho mai usato postgres.async, di solito uso jdbc+hikaricp)

Andrea Imparato12:05:33

oh non conoscevo hikaricp, sembra molto + potente di postgres.async

manuel12:05:13

provalo, perché così su 2 piedi a me la query sembra giusta, ma non conoscendo postgres.async non so che cosa succede sotto. 🙂

skuro13:05:00

+1 per pgsql + hikari + honey

🙌 3
manuel13:05:10

in realtà, honeysql va bene su questo progetto che è relativamente semplice per quanto riguarda l'interazione col DB. Ma quanto le query hanno sub-query, alias, e si fanno un po' più complesse, HugSQL è un'opzione migliore.

skuro14:05:13

meh. c'e' sempre un trade off: HoneySQL scala bene con la dinamicita' delle queries mentre scala male con la complessita' non-standardness dell'idioma SQL utilizzato, HugSQL scala viceversa. Avendo avuto crisi di rigetto da ORM, ero partigiano di soluzioni plain-sql come HugSQL. Oggigiorno HoneySQL lo trovo un default migliore visto che non ho mai avuto un progetto senza query dinamiche

👌 1
manuel14:05:46

in linea con te 🙂

Andrea Imparato18:05:47

e per interagire in async con il db cosa mi consigliate di fare?

Andrea Imparato18:05:25

per adesso faccio (async/<!! query! query-string) ma non so quanto corretto sia