Fork me on GitHub
Mark-James M.02:06:31

How can I prevent this from causing a casting error?

(defn tip-calculator []
  (println "Meal cost: ")
  (let [tip (read-line)]
    (println "Tip amount: " (* tip 0.15))))
I'm getting: class java.lang.String cannot be cast to class java.lang.Number (java.lang.String and java.lang.Number are in module java.base of loader 'bootstrap')


(Float/parseFloat (read-line)) ^ but that'll blow up on bad input


Don’t use floats, use Double/parseDouble

💯 1

ty! now I learned something too. 🙂

Mark-James M.03:06:16

Thanks everyone!


Is it a good practice to do something like this (into {} col)?


@mmakgaba Hard to say without a bit more context.


@seancorfield My collection looks like ([:foo "a"][:bar "b"])I want a map like {:foo "a", :bar "b"}


How did you get that data structure tho?


What is the problem you are actually trying to solve? I don't believe you started with a sequence of pairs. What did you start with?


From a js object. (js->clj obj)


I would have expected a JS object to convert to a hash map directly.


I just tried js->clj on a JS object and got a hash map. Did you do something to the result of that to produce a sequence of pairs?


Just getting props and than applied js->clj on the props


Please show more code.


Or at least explain what you mean by "props" and show how you are "getting props".


(defn my-fnc [prop]   (let [current-route (js->clj prop)]    current-route))  (my-fnc prop)


But where does prop come from, and what does it look like? Using into to get a map from a sequence of pairs is the standard way, but in this case it seems weird that you have a sequence of pairs to begin with


prop looks like #reitit.core.Match{:template "/", :data {},:result nil, :path-params {},:path "/", :query-params {},:parameters {:path {}, :query {}},:foo "a",  :bar "b"}


#reitit.core.Match isn't a javascript array or object, so you don't need to use js->clj on it


you can directly use it like

(:parameters prop)


Yeah, definitely over thought this one


you're welcome 🙂

Endre Bakken Stovner10:06:23

Is there a way to give types to keys in a map to optimize the speed? Like if I have :chromosome, :start and :end in a map I want the first to be a string, the second and third ints.


Do you have performance issues with maps?

Endre Bakken Stovner10:06:54

Yes, for a high-performance lib, they are going to be much slower than dataframes

Endre Bakken Stovner10:06:02

People will just stick with python then


So you want to optimize what? Lookups? Inserts? Memory efficiency? Did you profile your app? Maybe you just need libpython-clj

Endre Bakken Stovner10:06:59

I do not like using multiple runtimes


I don't have a profound advice but you may want to look at this:

👍 1

Still, it would be useful if you could share more details and specific profiling output to show us the bottleneck you want to optimize


It sounds like what you want is a contiguous packed data object without pointer dereferences. This can be done with a mutable ByteBuffer but you won't get much nicety from clojure or the jvm. I'll have an example in a moment.


(by pointer dereferences I don't mean usage of pointers (we have none explicitly) but rather objects represented by bytes elsewhere (non contiguous with the container) in heap.)


full disclosure: I've been obsessing about low level data representation and mechanical sympathy lately, so this code might be more about my desire to know how to optimally pack data, and less about the ideal answer to your question

(cmd)user=> (load-file "byte-buffer-hack.clj")
(cmd)user=> (def c (make-chromosome "hello, world" 1 2))
(cmd)user=> (chromosome c)
"hello, world"
(ins)user=> (start c)
(ins)user=> (end c)
(import (java.nio ByteBuffer))

(def int-bytes (constantly Integer/BYTES))
(def long-bytes (constantly Long/BYTES))

(defn pack-int
  [^ByteBuffer b ^Integer i]
  (.putInt b i))

(defn string-bytes
  [^String s]
  (+ Integer/BYTES
     (count (.getBytes s "UTF-8"))))

(defn pack-string
  [^ByteBuffer buf ^String s]
  (let [b (.getBytes s "UTF-8")
        size (count b)]
    (.putInt buf (int size))
    (.put buf b)))

(def chromosome-packing
  [{:lookup :start
    :measure int-bytes
    :insert pack-int}
   {:lookup :end
    :measure int-bytes
    :insert pack-int}
   {:lookup :chromosome
    :measure string-bytes
    :insert pack-string}])

(defn pack-into
  [pack-spec data]
  (let [insertables (map (fn [{:keys [lookup measure insert]}]
                           (let [datum (get data lookup)
                                 size (measure datum)]
                             {:datum datum
                              :insert insert
                              :size size}))
        bytes-needed (apply + (map :size insertables))
        buffer (ByteBuffer/allocate bytes-needed)]
    (doseq [{:keys [datum size insert]} insertables]
      (insert buffer datum))

(defprotocol IChromosomal
  (chromosome [this])
  (start [this])
  (end [this]))

(deftype Chromosome
  [^ByteBuffer chromo]
  (start [this]
    (.rewind chromo)
    (.getInt chromo 0))
  (end [this]
    (.rewind chromo)
    (.getInt chromo (+ 0 Integer/BYTES)))
  (chromosome [this]
    (.rewind chromo)
    (let [size-offset (+ 0 Integer/BYTES Integer/BYTES)
          size (.getInt chromo size-offset)
          str-offset (+ size-offset Integer/BYTES)
          raw (byte-array size)]
      (.position chromo str-offset)
      (.get chromo raw 0 size)
      (String. raw "UTF-8"))))

(defn make-chromosome
  [chromosome start end]
   (pack-into chromosome-packing
              {:chromosome chromosome
               :start start
               :end end})))
it's a lot of code, and not quite fully optimized, but the basic outline is sound. It uses one continuous array of bytes to store multiple values, and does everything via indexes and explicit sizes (as you would in assembly code) part of my inspiration comes from here (which is a more principled approach):

👆 1

It's probably bad code for most cases, and has all the downsides of very low level code, without losing all the downsides of high level code either. But it should be the basic blueprint for the fastest access of non-uniform data you can get on the jvm. Update/mutation functions are an exercise for the reader.

Endre Bakken Stovner07:07:56

Thanks, seems really useful 🙂 I made sure to save your reply in a private gist :)


I'm attempting to create a macro that wraps defrecord in cljs. Just a simple wrapper like this that does nothing is throwing an error: Can't take value of macro my-proj.core/defrecord-wrap

;; core.cljc
(defmacro defrecord-wrap
  [name fields & defs]
     (defrecord ~name
         [email protected])
;; core.cljs
(ns my-proj.core
  (:require-macros [my-proj.core]))
(defrecord-wrap Test [])
Is this possible in cljs?


changing (defrecord-wrap Test []) to (my-proj.core/defrecord-wrap Test []) fixes it

👍 2

Hey team noob question: when do you decide to use things like records, as opposed to sticking with the age old just use maps

👍 2

just use maps


I rarely reach for a record

☝️ 2

obviously if you need to implement a protocol, and need access to a data structure, records are required

👍 1

there are some special optimizations for accessing a record's declared fields, but that rarely factors into the decision for me

👍 1

I've always disliked the prescriptive form of this advice, but I can't argue much with the content


What is an example of a type modeling a domain value and one that does not? I'm not sure I fully understand the terminology.


I think I would probably say "entity" there


I think he's trying to compare the difference between modeling a user / company / whatever vs a custom collection type or something structural like that


Ah, makes sense now. Thanks!


handy graphic flow chart for when to use which forms of definition


Thanks @noisesmith! Loved that article. Would be curious to the step right before: “I need to define a type in clojure” Outside of interop, when would this be a good idea? Am trying to think through when something like defrecord would be a better abstraction decision then using map


I think that's covered by the chart "no interop" - > "no clojure protocols" -> use a hash-map


I guess there is a bit of difference of opinion (see thread above), but for most of us, that's the path we take


b/c records and maps have (almost) the same usage interface, you are generally going to mostly call them the same way, so start with the simpler form (maps)


records are simpler?!


I see, by simpler, do you mean it is simpler because the signature is more constrained? (One downside, where I think it can be considered more complex, is it’s use can be surprising. I.e if you evaluate new version of the record, all previous version can be stranded)


gah! I meant maps

❤️ 2

Ah! Okay makes a lot of sense.


Thanks for the context team!

Enrico Teterra18:06:46

hey folks, i just finished my first learning project in clojure - a simple todo app api with ring, clojure spec & using event sourcing (~ 70 lines + tests), would anyone be up for giving me some pointers, maybe a little code review? thanks a lot 🙂


If it helps, there's a #code-reviews channel (which has less traffic so folks who are happy to do code reviews are more likely to see a request like this).

Enrico Teterra18:06:26

thanks! i'll share it there as well


Why is it possible to use stuff like (clojure.string/starts-with? "11" "1") without importing, but I am unable to use my own stuff using the full path, (, for example?


things are available once they've been loaded, sometimes clojure's start up code loads clojure.string


you can't rely on that working, always require every namespace you use


import is specifically and only for classes in class-files, btw


Ah I see, thanks. I removed all my requires after switching to full path and the system kept working until I rebooted the web server hours later 😐


Noob question here: im getting some json and after it gets parsed it returns something like this ({"meta": {"id": 20}, "shortdef": ["box", "cash", "register"] }, {"meta": {"lang": "es"}, "shortdef": ["some", "more", "strings"] } ) How could I get all strings from shortdef?


(mapcat #(% "shortdef") l) where l is that list


Thank you, I was struggling with this a little too much.


#(% "shortdef") is a shorthand for (fn [a-map] (get a-map "shortdef")) - #() is a shorthand for fn, and calling a map like a function is the same as calling get on that map


I didnt know about get function, it'll come in handy for sure


if yo want seperate vectors instead of concatenation, use map instead of mapcat


Hey team, noob clojure question: How would you write a poor man's background process? I want to do something like: every minute, run a function

(defn run-worker [worker]
    (while true
      (Thread/sleep (:duration worker))
      ((:fn worker)))))
(Am just playing around, so don't want to use cron jobs if possible etc) The two things I am not sure about above a. By using future I am guessing this kicks off a new thread, so my Thread/sleep won't affect other things. Is that correct? b. There is no way to stop the worker. Any suggestions on how? c. Am thinking there may be a more idiomatic approach, but not sure what it would be. Thoughts much appreciated!


it only sleeps that thread, yes


instead of using while true you could check the cancellation status of the thread

❤️ 2

that way, someone cause use future-cancel on the object returned by future to stop the loop


also, if you don't have something that consumes the return value, always have a try/catch to log exceptions, as future swallows exceptions and rethrows on deref

👍 1

the danger is that you can have constantly failing tasks, and no diagnostics to indicate a problem

👍 1

Awesome! Agreed on try catch, and great idea re thread/isinterrupted. Love that this is so simple to do in clj. Wimpier languages would have forced me to use a cron job / queue


the advantage of the cron job is it persists outside vm runs

👍 1

and the future is using an executor with a queue under the hood IIRC


Noob q: if I did this, had a try catch etc, how likely is it that the thread would die? (For some reason I am not aware of atm) I.e could I reasonably expect this worker to run for a month?


it would be as likely to die as any other (while true ...) - I've found clojure / jvm programs very stable, able to run for monthts

❤️ 1

but it really depends on the code inside that loop :D

❤️ 1

heeck yeah!