Fork me on GitHub
#beginners
<
2020-01-22
>
Lukas09:01:47

Hi, I have a datomic sql transactor running

System started datomic:sql://<DB-NAME>?jdbc:, you may need to change the user and password parameters to work with your jdbc driver
and i want to connect to it now, i tried
(d/create-database "datomic:sql://<DB-NAME>?jdbc:")
=> Execution error (URISyntaxException) at .URI$Parser/fail (URI.java:2936).
Illegal character in opaque part at index 14: datomic:sql://<DB-NAME>?jdbc:
and
(d/create-database "datomic:")
=> Execution error (SQLException) at java.sql.DriverManager/getDriver (DriverManager.java:298).
No suitable driver
and a few more combinations. Could anybody explain to me how I make this work? Any help is greatly appreciated. :hugging_face:

mloughlin09:01:20

you could try asking the same question in #datomic

Lukas10:01:05

thx i give it a try

vm10:01:00

Hey folks, I have a question regarding reagent and SVG files. Is it possible to import an SVG file and inline it inside hiccup as a component? For example, in JS+React one would do something like

import MyIcon from './myIcon.svg';
...
<div>
  <MyIcon/>
</div>
If not, what are possible/usual ways how to handle SVG files without directly copying their content into .cljs files? Thank you

Lu10:01:52

Not that I’m aware of, but you can very easily wrap them yourself with a function component. I’d suggest you use the object tag if you need to access the SVG elements from js

Lu10:01:11

Can show you an example in a bit!

vm11:01:54

Thank you, would love to see it

Lu13:01:34

(defn foo []
  ;; with this code you can access all your svg elements in JS
  [:object
   {:data "/file.svg"
    :id "file"
    :width ...
    :height ...}
   ;; make sure you access the elements only when the svg has fully loaded
   :onLoad (fn []
             (let [svg-content (.-contentDocument
                                (js/document.getElementById "file"))]
               ;; now you can normally access your svg elements in JS and
               ;; perform any operation i.e.
               (set! (-> (.getElementById svg-content "my-svg-element-id")
                         .-style .-fill) "red")
               ...
               ))])

Lu13:01:49

^use this approach if you need to control elements within your svg

Lu13:01:36

if you only need to display the svg, then you could just do: [:img {:src "/file.svg"}]

zignd11:01:11

(ns xxx.data
  (:require [xxx.records]
            [xxx.records :refer [map->Account]])
  (:import [xxx.records Account]))
How do I require both the namespace and the auto generated map function for my Account record?

Ramon Rios15:01:14

Folks, just a question to check if my understanding is right: macro is a way of treat a value before it be evaluated by clojure evaluator. Am i right?

jakubl15:01:26

this is my understanding as well - but I too am completely new to clojure

Ramon Rios15:01:02

That's fine, we're all begginers.

manutter5115:01:55

Macros essentially re-write your source code just before the evaluator evaluates it. You have to be careful when you say a macro does something to a “value” because the only values available to a macro are the actual symbols and constants that make up your source code.

delaguardo15:01:26

not really, or at least it depends on value deffinition. In my mind it comes from the step in Read/Eval/Print/Loop. macroses are evaluated in Reading step and functions in Evaluating step.

manutter5115:01:40

(defmacro print-if-odd
  [some-value]
  (if (and (number? some-value)
           (odd? some-value))
    `(println ~some-value "is odd")))
=> #'user/print-if-odd
(let [x 5]
  (print-if-odd x))
=> nil
(print-if-odd 5)
5 is odd
=> nil
x is a runtime value, so at “macro time” it won’t exist yet. Calling (print-if-odd x) will be evaluated with some-value set to the literal symbol 'x , not its runtime value of 5.

manutter5115:01:30

But when you call (print-if-odd 5), you’re passing in 5 as a literal constant value in the source code, so then your macro will evaluate some-value as the literal 5.

manutter5115:01:01

(let [x 5]
  (macroexpand-1 '(print-if-odd x)))
=> nil
(macroexpand-1 '(print-if-odd 5))
=> (clojure.core/println 5 "is odd")

delaguardo15:01:00

(some-macro arg1 arg2) during reading will read arg1 and arg2 before executing some-macro. So for some-macro values of arg1 and arg2 will be the it’s form after reading. (some-function arg1 arg2) during evaluation will eval arg1 and arg2 before. So for some-functions valueas of arg1 and arg2 will be the values of them after evaluation

danielneal15:01:52

if arg1 and arg2 are macros themselves, will they be expanded before some-macro or after?

delaguardo15:01:23

before, because during the read only macroses are available to been executed. calculation of this (some-macro arg1 (another-macro arg2)) 1. some-macro is a macro so reader will “execute” it 2. to execute we need to read its arguments (`arg1` and (another-macro arg2)) 3. arg1 is not a macro so we can just read it 4. another-macro is a macro so the reader will “execute” it 5. to execute we need to read its arguments (`arg2`) 6. arg2 is not a macro so we can just read it 7. lets bubble up… another-macro executed because arg2 already expanded by reader 8. arg1 and result of (another-macro arg2) also expanded so we can execute (some-macro …) 9. done. all the steps above was done in reading step

delaguardo15:01:47

then eval step hits the ground

manutter5116:01:22

This is why the First Rule of Macros is “Don’t use macros” 😉

delaguardo16:01:25

nono) don’t be afraid of macro) the only difference - what arguments are for them, thats it.

sova-soars-the-sora16:01:17

i'd like to master macros, i don't know what i'd use them for

manutter5116:01:33

Definitely worth learning, and very useful once you get the hang of thinking like a compiler.

sova-soars-the-sora16:01:49

translate all my clojure code into java bytecode directly 😈 ?

delaguardo16:01:21

thats what i’m doing right now)

danielneal16:01:22

@delaguardo thanks, that was a useful explanation

sova-soars-the-sora16:01:45

macros are a programmatic way to edit code?

💯 2
sova-soars-the-sora16:01:16

i get the general idea but i still have a hard time stumbling into a real usecase. maybe my macro eyes are dusty

delaguardo16:01:26

(defmacro consK
  ([K] `(~K ()))
  ([el K] `(~K (cons ~el nil)))
  ([l el K] `(~K (cons ~el ~l))))

(defmacro assocK
  ([K] `(~K {}))
  ([[k v] K] `(~K (assoc {} ~k ~v)))
  ([m [k v] K] `(~K (assoc ~m ~k ~v))))

(declare list-to-cps const-to-cps)

(defmacro to-cps [n K]
  (cond
    (seq? n) `(list-to-cps ~n (fn [x#]
                                (prn "===DBG list" x#)
                                (~K x#)))
    (number? n) `(const-to-cps ~n (fn [x#]
                                    (prn "===DBG const" x#)
                                    (~K (if (= x# 2) ::panic! x#))))))

(defmacro const-to-cps [value K]
  `(~K ~value))

(defmacro list-to-cps [l-expr K]
  (cond
     (empty? l-expr)
     `(consK ~K)

     (= 1 (count l-expr))
     `(to-cps ~(first l-expr)
              (fn [el#]
                (consK el# ~K)))

     :else
     `(list-to-cps ~(rest l-expr)
                   (fn [l#]
                     (to-cps ~(first l-expr)
                             (fn [el#]
                               (consK l# el# ~K)))))))

Nate Sutton16:01:31

I find macros helpful in places where passing in a function is awkward

delaguardo16:01:51

as an example: this set of macros can transform clojure list into continuation passing style form

delaguardo16:01:23

(to-cps '(1 2 3) (fn [x]
                   (prn "===DBG" x)
                   x))
to quick try it out

Nate Sutton16:01:26

like if you were going to write your own if with clojure, without macros, you'd want to do

(if condition #(foo 1 2) (#foo 3 4))

manutter5116:01:28

I think the -> macro is a good example too

manutter5116:01:39

(source ->) in the repl to have a look at it.

Nate Sutton16:01:41

but with macros you can delay, or exclude entirely, execution of anything passed to the macro. so you can write something like

(if condition (foo 1 2) (foo 3 4))

Nate Sutton16:01:54

so basically it lets you skirt around normal code execution rules, and it does this by letting you read the arguments to the macros as data and then lets the macro generate new code using that data

manutter5116:01:58

When you do that, though, you have to remember that condition has to be a literal code value, not a runtime value, as in my first example above.

Nate Sutton16:01:54

there are definitely gotchas

Nate Sutton16:01:21

I meant to show that macros let you control and change execution

manutter5116:01:35

So for example you can write a macro that says “If I have an odd number of arguments, throw an exception” but you can’t (successfully) write a macro that says “If the current user is logged in, do X”

delaguardo16:01:19

(if (am-i-logged-in?) true false) why not?

manutter5116:01:36

You can use a macro to rewrite code into something that will do X if the current user is logged in, but at macro time the value of current-user is not defined.

mloughlin16:01:56

I'm pretty sure the diesel ORM for Rust implements a macro that connects to your database at compile time 😬

😱 1
Nate Sutton16:01:32

for generating code, right?

dpsutton16:01:53

if its what i'm thinking, they also typecheck your queries

mloughlin16:01:54

I read about that years ago at this point, so take my assertion with a grain of salt 😉

Nate Sutton16:01:54

I guess that's a trade-off to make

mloughlin16:01:56

the meander lib uses a lot of cool macros to do its work https://github.com/noprompt/meander

Eric Ihli19:01:34

What's a good way to inspect a Clojure object? I miss Pythons dir https://docs.python.org/3.5/library/functions.html#dir

andy.fingerhut19:01:27

(type x) tells you what type a value is. If it is a map, (keys x) tells you the keys. You can just type x at a REPL and see the value, although it might be bigger than you want to look at. (pprint x) can show it with nicer formatting, rather than one long line.

andy.fingerhut19:01:45

Cognitect's REBL has a graphical UI for exploring large and/or nested values in various ways.

andy.fingerhut19:01:15

I will mention clojure.inspector/inspect-tree but at the same time warn you that it has some limitations of what kinds of values it works with that can be annoying. REBL is more general purpose and polished by far.

andy.fingerhut19:01:58

Some Clojure IDEs have their own value inspectors they provide, but I have not used any of them.

bfabry19:01:16

for inspecting Java objects, bean is good

Eric Ihli19:01:04

Rad. Thanks. I'll dig into some of these.

bfabry19:01:39

or, if you want to really deeply inspect a java object, clojure.reflect/reflect will give you way more information than you could ever want. just remember it's pretty slow

seancorfield19:01:47

(ns-publics *ns*) will show you all the public symbols defined in the current namespace, as a hash map from symbols to Vars, so something like (keys (ns-publics 'clojure.set)) is probably easier to read.

kelveden10:01:35

For simple inspecting of clojure data structure like a map, I've found the reader tag #spy/p from https://github.com/dgrnbrg/spyscope invaluable.

mruzekw19:01:36

Is ISeq implemented for JS objects, arrays, and ES6 sets? I’m wondering if it would be possible to use keep immutability through functions rather than the data structures being immutable themselves

andy.fingerhut19:01:29

Every immutable data structure is only immutable because you choose not to do anything to it to modify it, even in Haskell. It is simply how many hoops you need to jump through to mutate it.

andy.fingerhut19:01:31

I haven't done any JS development myself, really, but my impression was that the issue in JS is that most code and libraries are written assuming it is OK to mutate things you pass in function calls, so you can write all the immutable library code you want, but as soon as you step outside of that, you are in danger?

mruzekw19:01:42

I guess that’s possible yes. Working in a pragmatic CLJS app I can’t imagine that risk could be avoided

andy.fingerhut19:01:45

I know that you can say "be diligent, and avoid calling that code". The thing is, that is a hoop that is so easy to jump through that it is hard not to jump through it, at least on a real project of any significant size or longevity. Not impossible, just challenging.

mruzekw19:01:46

The impetus for the question was around performance. I was talking with some who work in the creative tech / interactive graphics area. They said they moved away from CLJS because of performance issues with immutable data, even with transient

mruzekw19:01:55

So I wondered if we could “enforce” immutability for JS data structures directly

andy.fingerhut19:01:55

As far as your question on ISeq implemented for various types, folks in the #clojurescript and/or #cljs-dev channels would be likely to know more, more so than #beginners

andy.fingerhut19:01:22

I am sure it is possible to make someone jump through at least one more hoop to mutate something, in some cases, but if that hoop means you cannot call the JS library you want on your data, then it wouldn't achieve the goal, perhaps. I am speaking generally here, not from specific knowledge of JS and the possibilities, so don't take my word for it.

mruzekw19:01:54

Hmm, this is certainly something to think about. I would hope that I could find JS libs with immutable operations by default, but hard to ssay

andy.fingerhut19:01:46

#clojurescript is likely to find people who have thought about this more (and more importantly, some of them know JS and JS libraries, unlike me)

mruzekw19:01:33

Sure, I will ask in there

noisesmith21:01:17

using real mutation in tight loops (eg. graphics, signal processing) is a really common fallback (I've even had to resort to this for a graph in a social data analysis project to make our algorithm fast enough), but ideally you can code everything with clojure default immutable first, then translate the trouble spots to normal mutables in for loops

👍 1
noisesmith21:01:22

I don't think pretending javascript arrays are iseq helps much there - once you know where the problem is, do the ugly fast thing, if you are profiling to find hotspots and do this in a principled way it's not likely to spread to the rest of the codebase

👍 2
noisesmith21:01:25

(come to think of it, we had to use java mutable arrays for graphs on the backend .. and on the display side we did have to use js arrays specifically for the graph data display)

mruzekw22:01:58

@U051SS2EU What type of work were you doing with signal processing and graphics? And which methods did you use for real mutation?

noisesmith22:01:08

for graphics it was displaying graph connection data, for signal processing it was hobby work at home doing audio stuff (via a library called pink)

noisesmith22:01:29

for real mutation, you use the generic mutable data types the platform defines, via interop

noisesmith22:01:33

in fact, it was easier to rewrite the back end graph code in java (complete with a vector -> array translation step for incoming data) than it was to type hint and unbox all the stuff correctly in clojure code

noisesmith22:01:51

thanks to clojure's excellent interop, both are easy to do

mruzekw22:01:49

Nice, thanks for the details

didibus03:01:48

It also is possible to extend them to support ISeq, see the other section: https://www.rubberducking.com/2018/04/overview-of-clojurescript-110-features.html#type-extension

didibus04:01:32

(defn it-seq [iterator]
  (let [next (.next iterator)]
    (when-not (.-done next)
      (lazy-seq 
        (cons
          (.-value next)
          (it-seq iterator))))))

(extend-type object
  ISeqable
  (-seq
    [this]
    (it-seq (.values this))))

(map inc (doto (js/Map.) (.set 1 1) (.set 2 2) (.set 3 3)))

didibus04:01:00

Might be a few more protocols to implement, but this gives you first, second, nth, next, rest, seq which already covers most things like map, filter, remove, concat, etc.

didibus04:01:00

It'll work with every object that support .values returning an iterator

noisesmith18:01:21

there are drawbacks to plugging mutables into code written for immutable objects though, and the root issue here is best addressed by not using the ISeq api

💯 1
didibus19:01:40

Ya, I agree, but, you know, if you're really going for optimization and trying to reuse the same APIs of your existing logic or wtv, if you keep it local to a namespace and know what you're doing etc. Basically, I gave you a gun, don't shoot your foot with it 😛