This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-10-26
Channels
- # announcements (28)
- # asami (13)
- # babashka (10)
- # beginners (170)
- # boot (1)
- # calva (35)
- # cider (21)
- # circleci (13)
- # clara (6)
- # clj-http (1)
- # clj-kondo (29)
- # cljdoc (5)
- # clojure (89)
- # clojure-czech (2)
- # clojure-europe (20)
- # clojure-france (16)
- # clojure-nl (6)
- # clojure-uk (5)
- # clojurescript (80)
- # community-development (6)
- # conjure (13)
- # cursive (18)
- # datascript (9)
- # datomic (1)
- # duct (1)
- # gratitude (2)
- # helix (7)
- # jobs (2)
- # kaocha (3)
- # lsp (22)
- # malli (5)
- # meander (1)
- # other-languages (34)
- # pathom (18)
- # polylith (24)
- # quil (10)
- # re-frame (5)
- # releases (1)
- # remote-jobs (4)
- # reveal (7)
- # shadow-cljs (8)
- # tools-deps (53)
I constantly find myself writing expressions and eval'ing them just to check/test something and then delete the expressions. I quite enjoy it, but I just want to make sure I'm not performing some type of anti-pattern. Maybe this type of thing is better suited to tests?
people say to never type in the repl but i don't get that. sounds perfect for actually typing in a repl. But making the comment blocks and sending things to the repl works just as fine. You aren't doing anything wrong and its how i often work
Nice. I'm typically writing things directly in my .cljs files and then just running my editor command to eval a form
https://practical.li/clojure/reference/clojure-syntax/comments.html#rich-comment-blocks
@seancorfield had some videos on working with the repl that may be interesting as well
hello, how do I resolve this conflict?
:import
[ File FileInputStream InputStreamReader]
[com.google.api.services.drive.model File]
for File?The only thing import does is make it so you can refer to a class by its short name, so you can just not import File and refer to it by java.io.File
How do I sort this set by the map in the vector?
(def jobfiles #{[[:samtools-index {:genome "hg38", :sample "C"}] "bam/sorted.bam.bai"]
[[:samtools-sort {:genome "hg38", :sample "C"}] "bam/sorted.bam"]
[[:samtools-index {:genome "hg38", :sample "A"}] "bam/sorted.bam.bai"]
[[:samtools-sort {:genome "hg38", :sample "B"}] "bam/sorted.bam"]
[[:samtools-sort {:genome "hg38", :sample "A"}] "bam/sorted.bam"]
[[:samtools-index {:genome "hg38", :sample "B"}] "bam/sorted.bam.bai"]})
I've tried many things like:
(map (comp second first) jobfiles)
({:genome "hg38", :sample "C"}
{:genome "hg38", :sample "C"}
{:genome "hg38", :sample "A"}
{:genome "hg38", :sample "B"}
{:genome "hg38", :sample "A"}
{:genome "hg38", :sample "B"})
(sort-by (comp second first) jobfiles)
;; Execution error (ClassCastException) at java.util.TimSort/countRunAndMakeAscending (TimSort.java:355).
;; class clojure.lang.APersistentMap$ValSeq cannot be cast to class java.lang.Comparable (clojure.lang.APersistentMap$ValSeq is in unnamed module of loader 'app'; java.lang.Comparable is in module java.base of loader 'bootstrap')
Maps are not sortable; you could convert it to e.g. a sequence to sort it, but that would be potentially ambiguous. What is your expected behavior?
If you want to sort by specific keys, maybe something like this:
(sort-by
(fn [[_tag m] _file]
((juxt :genome :sample) m))
jobfiles)
I do not want to sort it by specific keys, but by the vals of the map 🙂 Thanks btw
As long as the order is the same for every map it does not really matter.
remember that maps can theoretically have different set of keys and it's domain specific whether that is something that concerns you
In this case the fact that the keys might be different does not matter
so, just for fun here are some alternative sorting schemes: :)
(vals m) ;; ambiguous
(sort (vals m))
(mapv #(get m %) (sort (keys m)))
Or if you want to be agnostic of the particular keys in the map you can do:
(sort-by #(let [m (-> % first second)] ((apply juxt (keys m)) m)) jobfiles)
You can also choose to sort by the alphabetical sort of the keys in the map like so:
(sort-by #(let [m (-> % first second)] ((apply juxt (sort (keys m))) m)) jobfiles)
What is the best way to call get-forecast
here
it schould be done after the handle-response-coordinates
@U0EGWJE3E you can actually have handle-response-coordinates
call get-forecast!
after it updates app-state. That way you can coordinate their order
aside: instead of
(swap! app-state
update-in [:latitude] (constantly lat))
you can just use:
(swap! app-state
assoc-in [:latitude] lat)
and it's best to not call swap!
more than once, if not necessary; so I would revise your 2 swaps to a single call:
(swap! app-state
(fn [state]
(-> state
(assoc-in [:latitude] lat)
(assoc-in [:longtitude] lon))))
@U05476190 oke but i think I have to put the code for making the coordinates under the code to find the weather data or im I wrong ?
(defn get-forecast! ..)
(defn get-coordinates ..) ;; needs to be after, because it calls get-forecast!
(.. some code that calls get-coordinates ..)
I see this now at the dev console
Object { meta: null, cnt: 3, arr: (6) […], __hash: null, "cljs$lang$protocol_mask$partition0$": 16647951, "cljs$lang$protocol_mask$partition1$": 139268 }
__hash: null
arr: Array(6) [ {…}, 0, {…}, … ]
looks like you're missing pretty-printing in Dev tools; are you using Chrome? https://github.com/binaryage/cljs-devtools
check out this for more info: https://practical.li/clojurescript/install/browser-devtools.html
chips. frustating. I do something wrong here
(def raw-data (atom nil))
(defn get-coordinates []
(let [zipcode (:zipcode "hengelo")]
(ajax/GET ""
{:params {"q" zipcode
"units" "metric"
"appid" api-key}
:handler (fn [resp] (reset! raw-data resp))})))
(comment
get-coordinates
@raw-data
)
the url was wrong but also if I change things to
(defn get-coordinates []
(let [zipcode (:zipcode "hengelo")]
(ajax/GET ""
{:params {"q" zipcode
"appid" api-key}
:handler (fn [resp] (reset! raw-data resp))})))
according to this page : https://openweathermap.org/api/geocoding-api@U05476190 do you see something wierd ?
(def raw-data (atom nil))
(defn get-coordinates []
(let [zipcode (:zipcode "hengelo")]
(ajax/GET ""
{:params {"q" zipcode
"appid" api-key}
:handler (fn [resp] (reset! raw-data resp))})))
(comment
get-coordinates
@raw-data
)
http://api.openweathermap.org/geo/1.0/direct?q=London&limit=5&appid={API key}`` even the example call does not work.
$ curl -v -H "Accept: application/json" '
<html><head><title>Loading...</title></head><body><script type='text/javascript'>window.location.replace('
maybe after a good night sleep I see what is wrong here
(defn get-coordinates []
(let [zipcode (:zipcode "Hengelo")]
(ajax/GET ""
{:params {"q" zipcode
"appid" api-key}
:handler (fn [resp] (reset! raw-data resp))})))
(comment
get-coordinates
@raw-data
)
oh i got it api key is given to me but it takes time to activate. now the london example call works.
code so far
(ns ^:figwheel-hooks learn-cljs.weather
(:require
[goog.dom :as gdom]
[reagent.dom :as rdom]
[reagent.core :as r]
[ajax.core :as ajax]))
(defonce app-state (r/atom {:title "WhichWeather"
:latitude 0
:longtitude 0
:zip-code ""
:temperatures {:today {:label "Today"
:value nil}
:tomorrow {:label "Tomorrow"
:value nil}}}))
(defn handle-response [resp]
(let [today (get-in resp ["hourly" 1 "temp"])
tomorrow (get-in resp ["hourly" 5 "temp"])]
(swap! app-state
update-in [:temperatures :today :value] (constantly today))
(swap! app-state
update-in [:temperatures :tomorrow :value] (constantly tomorrow))))
(defn handle-response-coordinates[resp]
(let [lat (get-in resp ["coord" "lat"])
lon (get-in resp ["coord" "lon"])]
(swap! app-state
update-in [:latitude] (constantly lat))
(swap! app-state
update-in [:longtitude] (constantly lon))))
(def api-key "fa9930ab5e2a87f9770d1c90d68f9da1")
(defn get-forecast! [] ;; <3>
(let [lat (:latitude @app-state)
lon (:longtitude @app-state)]
(ajax/GET ""
{:params {"lat" lat
"lon" long
"units" "metric" ;; alternatively, use "metric"
"appid" api-key}
:handler handle-response})))
(defn get-coordinates []
(let [zipcode (:zipcode @app-state)]
(ajax/GET ""
{:params {"q" zipcode
"units" "metric"
"appid" api-key}
:handler handle-response-coordinates})))
(defn title []
[:h1 (:title @app-state)])
(defn temperature [temp]
[:div {:class "temperature"}
[:div {:class "value"}
(:value temp)]
[:h2 (:label temp)]])
(defn postal-code []
[:div {:class "postal-code"}
[:h3 "Enter your postal code"]
[:input {:type "text"
:placeholder "Postal Code"
:value (:postal-code @app-state)
:on-change #(swap! app-state assoc :postal-code (-> % .-target .-value))}]
[:button {:on-click get-coordinates} "Go"]])
(defn app []
[:div {:class "app"}
[title]
[:div {:class "temperatures"}
(for [temp (vals (:temperatures @app-state))]
[temperature temp])]
[postal-code]])
(defn mount-app-element []
(rdom/render [app] (gdom/getElement "app")))
(mount-app-element)
(defn ^:after-load on-reload []
(mount-app-element))
when calling grep with clojure.java.shell
it never finds something. I have the suspicion there is some jvm reason for this?
I'm trying to implement some simple bb program that involves grepping over a big directory
I feel really stupid asking this, because the problem is so simple, but I can't wrap my head around how to solve it in clojure. I have a list of integers of which I want to return the index of the highest integer. Simple to do in oop. Any tips? I can't use higher-order functions, because they don't keep track of the indexes?
Some ideas: if you reduce
over the collection, you can keep track of the existing index, as well as the index and value of the highest integer.
Not sure how I would do that? I would at least have to manipulate the list first so each value is paired with its index first
Or you can use map-indexed
to return a list of tuples [value index]
and then e.g. sort and return the highest value
No, in case of reduce you are free to keep an accumulator and stuff whatever you want into it
or another way is to turn it into 2 steps:
(.indexOf vector-of-ints (apply max vector-of-ints))
Yes but with reduce, how do i know what index I am currently looking at? If I reduce over the vector [1 4 6 8 9 2] I cannot know that the current number I am looking at is at a specific index?
You keep the index of the current highest value and the value in a vector as the accumulator
oh wow it's a hard reduction to do, but yeah you'd have to have the highest-value, previous-index, and highest-value-index in the accumulator.
yeah, something to grok what we mean:
(reduce (fn [acc n]
(if acc
(let [[idx max-idx max-val] acc]
(if (> n max-val)
[(inc idx) idx n]
[(inc idx) max-idx max-val]))
[0 0 n]))
nil
[1 4 6 8 9 2])
user> (def vector-of-ints [1 2 3 5 7 2 1 10 4 20 42 4 3 1])
user> (reduce
(fn [[index highest-val previous-index] current-val]
(let [current-index (inc previous-index)]
(if (> current-val highest-val)
[current-index current-val current-index]
[index highest-val current-index])))
[0 0 -1]
vector-of-ints)
;;=> [10 42 13]
user> (get vector-of-ints 10)
the reduction is the same logic as you would write in an imperative language, but you have to put all your state in a data structure and return it from a function rather than use lexical scope to refer to variables ... so it looks confusing ... is that a useful perspective?
You could also use a map rather than a vector if you wanted to name the "variables" in the state ;)
this was my stab at it with reduce
(:idx (reduce (fn [{:keys [mx i] :as r} x]
(-> r
(cond-> (some-> mx (< x)) (assoc :idx i :mx x))
(cond-> (nil? mx) (assoc :mx x))
(update :i (fnil inc 0))))
nil
[1 4 6 8 9 2]))
here's a different approach:
(let [[idx val] (->> [1 4 6 8 9 2]
(map-indexed vector)
(sort-by second >)
first)]
idx)
so many ways to go about this:
(first (apply max-key second (map-indexed vector '(2 1 6 5 4))))
Here's how I did it:
(defn index-of-highest
[some-seq]
(loop [idx 0
biggest-known-number -1E100
idx-of-biggest-known -1]
(if (>= idx (count some-seq)) ;; If index is beyond bounds of some-seq, return
idx-of-biggest-known
(if (< biggest-known-number (get some-seq idx))
(recur (+ idx 1) (get some-seq idx) idx)
(recur (+ idx 1) biggest-known-number idx-of-biggest-known)))))
I chose the loop syntax because it gives you the illusion of TCO in Clojure.
@U05476190 Dude that is next level
sure, I edited it with some more random numbers (I'd prefer something that you can eval directly in the repl)
Transducers version 🙂
(defn- rf [acc x] (max-key peek acc x))
(defn max-idx
[xs]
(peek
(reduce rf
(->Eduction (map-indexed vector) xs))))
Here's an "illegal" version
(defn max-idx2
^long [^Iterable xs]
(let [it (.iterator xs)]
(loop [i 0
-max-idx i
-max (unchecked-long (.next it))]
(if (.hasNext it)
(let [x (unchecked-long (.next it))
i (unchecked-inc i)]
(if (> x -max)
(recur i i x)
(recur i -max-idx -max)))
-max-idx))))
With checked math it's ~5x faster than my max-idx
With uncheck math it's ~22x faster(defn max-index
[nums]
(apply max-key #(nth nums %) (range (count nums))))
I’d go for something like this or @UGFL22X0Q’s two-step version
Edit: Probably not very efficient for lists (vectors should be fine).Several (though certainly not all) approaches that people consider FP will do that
The map-indexed
fn might be of use here.
Hello, am trying to figure out how to use environment values for migratus with the leiningen plugin.
I followed the instructions on https://github.com/yogthos/migratus#quick-start-with-leiningen
I have the database password in an environment variable. When I evaluate (environ.core/env :db-password)
, I get the value for it, but when I run lein migratus migrate
I get,
migrating all outstanding migrations
Syntax error (ClassNotFoundException) compiling at (/private/var/folders/d_/vygvcb251gl8by5c8m1_wlsr0000gn/T/form-init9053620023813719463.clj:1:341).
environ.core
Full report at:
/var/folders/d_/vygvcb251gl8by5c8m1_wlsr0000gn/T/clojure-5134916087895186035.edn
Subprocess failed (exit code: 1)
If some one has encountered this before or has any idea why this is, I would highly appreciate the help. Thanks.
p.s. my project.clj file looks like this.
(defproject
playground "0.1.0"
:description "Example project"
:url ""
:dependencies [[com.github.seancorfield/next.jdbc "1.2.731"]
[environ "1.2.0"]
[migratus "1.3.5"]
[org.clojure/clojure "1.10.3"]
[org.postgresql/postgresql "42.3.0"]]
:min-lein-version "2.0.0"
:main ^:skip-aot playground.core
:plugins [[lein-environ "1.2.0"]
[migratus-lein "0.7.3"]]
:migratus {:store :database
:migration-dir "migrations"
:db {:dbtype "postgres"
:dbname "playground"
:user "playground"
:password (environ.core/env :db-password)
:port 5433}}
:profiles
{:uberjar
{:aot :all
:uberjar-name "app-standalone.jar"}
:dev [:project/dev :profiles/dev]
:test [:project/test :profiles/test]
:project/dev
{:plugins []}
:project/test {}
:profiles/dev {}
:profiles/test {}})
Full report at:
/var/folders/d_/vygvcb251gl8by5c8m1_wlsr0000gn/T/clojure-5134916087895186035.edn
yes and did not understand what was causing it.
{:clojure.main/message
"Syntax error (ClassNotFoundException) compiling at (/private/var/folders/d_/vygvcb251gl8by5c8m1_wlsr0000gn/T/form-init9053620023813719463.clj:1:341).\nenviron.core\n",
:clojure.main/triage
{:clojure.error/phase :compile-syntax-check,
:clojure.error/line 1,
:clojure.error/column 341,
:clojure.error/source "form-init9053620023813719463.clj",
:clojure.error/path
"/private/var/folders/d_/vygvcb251gl8by5c8m1_wlsr0000gn/T/form-init9053620023813719463.clj",
:clojure.error/class java.lang.ClassNotFoundException,
:clojure.error/cause "environ.core"},
:clojure.main/trace
{:via
[{:type clojure.lang.Compiler$CompilerException,
:message
"Syntax error compiling at (/private/var/folders/d_/vygvcb251gl8by5c8m1_wlsr0000gn/T/form-init9053620023813719463.clj:1:341).",
:data
{:clojure.error/phase :compile-syntax-check,
:clojure.error/line 1,
:clojure.error/column 341,
:clojure.error/source
"/private/var/folders/d_/vygvcb251gl8by5c8m1_wlsr0000gn/T/form-init9053620023813719463.clj"},
:at [clojure.lang.Compiler analyzeSeq "Compiler.java" 7119]}
{:type java.lang.ClassNotFoundException,
:message "environ.core",
:at
[java.net.URLClassLoader findClass "URLClassLoader.java" 440]}],
:trace
[[java.net.URLClassLoader findClass "URLClassLoader.java" 440]
[clojure.lang.DynamicClassLoader
findClass
"DynamicClassLoader.java"
69]
[java.lang.ClassLoader loadClass "ClassLoader.java" 587]
[clojure.lang.DynamicClassLoader
loadClass
"DynamicClassLoader.java"
77]
[java.lang.ClassLoader loadClass "ClassLoader.java" 520]
[java.lang.Class forName0 "Class.java" -2]
[java.lang.Class forName "Class.java" 467]
[clojure.lang.RT classForName "RT.java" 2212]
[clojure.lang.RT classForNameNonLoading "RT.java" 2225]
[clojure.lang.Compiler$HostExpr maybeClass "Compiler.java" 1041]
[clojure.lang.Compiler macroexpand1 "Compiler.java" 7049]
[clojure.lang.Compiler analyzeSeq "Compiler.java" 7097]
[clojure.lang.Compiler analyze "Compiler.java" 6793]
[clojure.lang.Compiler analyze "Compiler.java" 6749]
[clojure.lang.Compiler$MapExpr parse "Compiler.java" 3108]
[clojure.lang.Compiler analyze "Compiler.java" 6801]
[clojure.lang.Compiler analyze "Compiler.java" 6749]
[clojure.lang.Compiler$MapExpr parse "Compiler.java" 3108]
[clojure.lang.Compiler analyze "Compiler.java" 6801]
[clojure.lang.Compiler analyze "Compiler.java" 6749]
[clojure.lang.Compiler$InvokeExpr parse "Compiler.java" 3892]
[clojure.lang.Compiler analyzeSeq "Compiler.java" 7113]
[clojure.lang.Compiler analyze "Compiler.java" 6793]
[clojure.lang.Compiler analyze "Compiler.java" 6749]
[clojure.lang.Compiler$BodyExpr$Parser parse "Compiler.java" 6124]
[clojure.lang.Compiler$FnMethod parse "Compiler.java" 5471]
[clojure.lang.Compiler$FnExpr parse "Compiler.java" 4033]
[clojure.lang.Compiler analyzeSeq "Compiler.java" 7109]
[clojure.lang.Compiler analyze "Compiler.java" 6793]
[clojure.lang.Compiler eval "Compiler.java" 7178]
[clojure.lang.Compiler eval "Compiler.java" 7171]
[clojure.lang.Compiler load "Compiler.java" 7640]
[clojure.lang.Compiler loadFile "Compiler.java" 7578]
[clojure.main$load_script invokeStatic "main.clj" 475]
[clojure.main$init_opt invokeStatic "main.clj" 477]
[clojure.main$init_opt invoke "main.clj" 477]
[clojure.main$initialize invokeStatic "main.clj" 508]
[clojure.main$null_opt invokeStatic "main.clj" 542]
[clojure.main$null_opt invoke "main.clj" 539]
[clojure.main$main invokeStatic "main.clj" 664]
[clojure.main$main doInvoke "main.clj" 616]
[clojure.lang.RestFn applyTo "RestFn.java" 137]
[clojure.lang.Var applyTo "Var.java" 705]
[clojure.main main "main.java" 40]],
:cause "environ.core",
:phase :compile-syntax-check}}
:type java.lang.ClassNotFoundException,
:message "environ.core",
you dont necessarily have access to environ/core in project.clj settings{:store :database
:migration-dir "migrations"
:db ~(get (System/getenv) "DATABASE_URL")}
see if this works. from the example.Thanks for the tip !!
(get (System/getenv) "DB_PASSWORD")
returns nil and lein migratus migrate
gives
SEVERE: Error creating DB connection for {:dbtype "postgres", :dbname "playground", :user "playground", :password nil, :port 5433}
org.postgresql.util.PSQLException: The server requested SCRAM-based authentication, but the password is an empty string.
this is my profiles.clj
{:profiles/dev {:env {:db-password "playground"}}}
and this is .lein-env
{:db-password "playground"}
oh this sounds like how lein setup env. but is this the same as system environment variable like you'd do with BASH/ZSH?
https://stackoverflow.com/questions/21517583/cant-access-environment-variables-in-leiningen-project-clj-with-environ https://github.com/weavejester/environ do you need lein-environ?
When I add env variable in terminal like export DB_PASSWORD=playground
and use :password (get (System/getenv) "DB_PASSWORD")
in project.clj, the lein command lein migratus migrate
works as expected. But am not able to read env variables in project.clj which was added from profiles.clj.
I am also using lein environ plugin in project.clj like
:plugins [[lein-environ "1.2.0"]
[migratus-lein "0.7.3"]]
Adding env variable into terminal each time I need them is not an ideal solution for me. I would like to find out a way so that the env variable is available every time I start the project. And that is why I used profiles.clj and environ library with lein-environ plugin, but unfortunately environ cannot be used from project.clj
@UP82LQR9N I appreciate your help in this so far 🙂I took this random kotlin function from one of our projects at work (of which I don’t have context of), and tried to re-write it in Clojure (not caring if it’s exactly correct) to just try to get a feel for what it would look like. I pretty much transferred line for line of how I read it imperatively and it came out with more lines of code and a lot harder to read. I’m guessing my problem was not truly understanding the function before writing it in Clojure. I wanted to share here to see if I could get any valuable feedback of some things I could’ve done better. Kotlin:
fun AbstractEnvironment.getPropertiesByPrefix(prefix: String): Map<String, Map<String, Any?>> {
val propertyMap = mutableMapOf<String, MutableMap<String, Any?>>()
val environmentPrefix = prefix.toUpperCase().replace(".", "_")
// reverse order to get correct overrides
propertySources.reversed().forEach { source ->
// If this is an environment variable or system property, we want to compare with the environmentPrefix
val compareAsEnvironment = source.name == "systemEnvironment" || source.name == "systemProperties"
(source as? EnumerablePropertySource)?.let { enumerableSource ->
enumerableSource.propertyNames.forEach { propertyName ->
if (propertyName.startsWith(prefix) || (compareAsEnvironment && propertyName.toUpperCase().startsWith(environmentPrefix))) {
// If this is an env, replace the "_" with "."
val name = if (compareAsEnvironment) propertyName.replace("_", ".") else propertyName
val key = name.substring(prefix.length +1, name.indexOf('.', prefix.length +1))
val map = propertyMap.getOrPut(key) { mutableMapOf() }
map[name.substring("$prefix.$key.".length)] = enumerableSource.getProperty(name)
}
}
}
}
return propertyMap
}
Clojure:
(defn get-properties-by-prefix [prefix property-sources]
(let [env-prefix (-> prefix .toUpperCase (.replace "." "_"))]
(loop [[source & rest] (reverse property-sources)
property-map {}]
(if-not rest property-map)
(let [cmpAsEnv (or (= "systemEnvironment" (:name source))
(= "systemProperties" (:name source)))]
(loop [[propertyName & rest] (:propertNames source)]
(if (or (-> propertyName (.startsWith prefix))
(and cmpAsEnv
(-> propertyName .toUpperCase (.startsWith env-prefix))))
(let [name (if cmpAsEnv (.replace propertyName "_" ".") propertyName)
key (-> name (.substring (+ 1 (.length prefix)) (.indexOf name "." (+ 1 (.length prefix)))))
map (key property-map {})]
(if rest
(recur rest (assoc property-map (-> name (.substring (.length (str prefix "." key "."))))
(:name source))))))))
(recur rest))))
Is my understanding correct that the kotlin version is supposed to, given prefix "uk", transform something like (using clojure literal syntax)
[{:name "systemEnvironment"
:propertyNames ["UK_EMAIL_ADDRESS_DETAILS" "DE_EMAIL_ADDRESS" "uk.full.nick"]
:getProperty {"UK_EMAIL_ADDRESS_DETAILS" 1 "DE_EMAIL_ADDRESS" 2 "uk.full.nick" 0}}
{:name "somewhereElse"
:propertyNames ["uk.email.address" "uk.full.name" "de.full.name" "UK_WAIT"]
:getProperty {"uk.email.address" 3 "uk.full.name" 4 "de.full.name" 5 "UK_WAIT" 0}}]
into
{{"EMAIL" {"ADDRESS.DETAILS" 1}
"email" {"address" 3}
"full" {"name" 4 "nick" 0}}
?
If so, does enumerableSource.getProperty(name)
actually return anything but null in case compareAsEnvironment = true
? (I'd expect to see getProperty(propertyName)
there)That isn't clojure.core/key, that is the key bound right before it, which isn't even a function, so calling it makes even less sense
I think that's an unfair treatment. To me, even if it can't be run, it does show that merely translating an imperative function from Java/Kotlin to clojure won't usually yield good results. That's an insight with its own merit!
It also leads to more good questions like: • what would an idiomatic translation look like? • why is the imperative version more verbose in clojure when clojure is typically known for its succinctness? • does that mean algorithms that require imperative side-effects are typically more verbose in clojure?
I think the if-not
is supposed to also include the let
as the else branch.
so then it would be a loop
that accumulates into property-map
I never said it was.
it's an imperative translation.
I guess my main point is that since this is the #beginners channel, we can give better feedback than "this is completely broken", which I don't think is a fair assessment.
They are trying to compare it to some kotlin code, if we are going to allow comparisons of completely broke code, at what point does one draw the line?
E.g. is nil
a valid translation? Does it show how great clojure is? I mean it's so small
If the given translation is acceptable for comparisons, to the point where you are trying to draw conclusions about verbosity, and nil
isn't acceptable, where is the line?
> They are trying to compare it to some kotlin code, if we are going to allow comparisons of completely broke code, at what point does one draw the line?
I think any honest attempt is good enough to learn from. If nil
was the best attempt someone could produce, I would still try to offer help (probably by pointing them to better self-starting resources).
Using negative superlatives is the absolute worst kind of feedback :p
Hey thanks for understanding phron. Still trying to grasp the difference in paradigms and how I would go about translating from one to another. I’ll try again tomorrow with a different function that I do understand
My comments about doseq, assoc, etc are all aimed at the suggestion that this code is imperative
Written idiomatically, the code I would likely be some kind of reduce building a map, maybe a nested map using assoc-in, hard to tell
A straight forward imperative translation of the kotlin would do things like use iterators, because kotlins foreach method is sugar over iterators
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/for-each.html has an example of what using a raw iterator looks like vs a kotlin foreach (same number of lines amusingly)
I will say since there is a paradigm shift, translating function can be a bad way to get a feel for the different paradigm. It might be better you start with the problem and try to find out how to implement it in Clojure instead, doing so with little inspiration from the original or it might even lead you down the wrong path.
I'd recommend you work from a test instead. Like write the input and the output you expect. And then work at the REPL to try and find a way in Clojure given that input to return the desired output.
But also if I look at this Kotlin, the challenge is that in Clojure you would not have the data modeled the way it is in Kotlin either, so the feel of using Clojure would differ even in the shape and structure you'd be operating over.