This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-12-11
Channels
- # adventofcode (108)
- # announcements (4)
- # aws (11)
- # babashka (39)
- # beginners (199)
- # calva (12)
- # clj-kondo (17)
- # cljs-dev (1)
- # clojure (115)
- # clojure-dev (9)
- # clojure-europe (98)
- # clojure-italy (17)
- # clojure-nl (4)
- # clojure-norway (3)
- # clojure-seattle (7)
- # clojure-sweden (6)
- # clojure-switzerland (5)
- # clojure-uk (15)
- # clojurescript (41)
- # code-reviews (36)
- # conjure (7)
- # datomic (1)
- # emacs (1)
- # events (1)
- # fulcro (26)
- # graalvm (2)
- # helix (35)
- # jackdaw (2)
- # jobs (9)
- # jobs-discuss (5)
- # lambdaisland (2)
- # meander (24)
- # off-topic (80)
- # pathom (22)
- # pedestal (1)
- # portal (20)
- # re-frame (3)
- # releases (1)
- # reveal (13)
- # rewrite-clj (1)
- # shadow-cljs (8)
- # specter (5)
- # sql (4)
Hi everyone I was reading about final in a Java book and how java.lang.String is a final class so it can't be extended and the book was explaining about how it would be dangerous and chaotic to have alternative implementations of String be accepted polymorphically by methods but Clojure's polymorphism allows you to extend even final classes, if so then wouldn't that be dangerous also or is there something I'm not understanding that makes this OK to do?
@flyingpython In Java, polymorphism is intrinsically tied to inheritance. In Clojure, that is not the case.
Clojure's polymorphism is, essentially, a la carte. Protocols allow you to associate type-based dispatch with additional functionality.
Would it be fair to say that Clojure's protocols would allow you to create dangerous and chaotic protocol implementations, if you wished, perhaps in a similar way that the Java book claims that extending java.lang.String might be? At least, it doesn't seem like Clojure somehow prevent that from happening.
I agree that some kinds of danger and chaos are avoided by not having implementation inheritance -- just not all of them. There are so many kinds of danger and chaos, after all š
One person's chaos is another person's ordered functionality... š
This is why you should only extend protocols you own or types you own. But, sure, you can write dangerous, chaotic code with Clojure (just like any language, even Java, despite final
š )
Ah OK thanks for the explanation guys, so I guess it is mostly avoided by convention of not doing unwise things, it seems super cool that you can do that in Clojure though š
And it's probably worth noting that next.jdbc
extends Sourcable
to String
so that you can polymorphically call get-datasource
on a JDBC URL as a string, just like you can call it on an associative data structure.
Wow that's super cool!
For example, in next.jdbc
, it has the concepts of "sourceable" -- something you can make a datasource from -- and "connectable" -- something you can make a connection from. That allows you to treat a number of things in a consistent, polymorphic, manner and work in a natural way with things that perhaps weren't designed with the affordances you need.
Thanks for the explanations, yes the book I am working through on Clojure said working with databases is a common use case for Clojure's polymorphism
clj::ground_up.chapter5=> (ns ground-up.chapter5)
nil
clj::ground-up.chapter5=>
; Syntax error compiling at (chapter5.clj:38:1).
; Unable to resolve symbol: defn in this context
code :
(defn palindrome?
"Checks if a word is a palingdrome"
[word]
(= (seq word) (reverse word)))
In a cider repl, how do I print a ācomplexā map in a def-friendly way? I would like to be able to cut & paste from repl into a def in testing code, but I run into issues like lists being (1 2 3) in the output instead of ā(1 2 3) or (list 1 2 3), for example.
Every now and then I just quote such "lists" manually.
If this is for debugging/inspection you can use cider inspector or debugger and press d
when you're focused on the right object - it will prompt you for a var name.
(walk/postwalk (fn [x] (if (and (seq? x) (not (vector? x)))
`(~'list ~@x)
x))
{:a (range 3)
:b {:c (range 3)}
:d (map inc (range 3))
:e [(map inc (range 3))]})
bit hacky but can work for yaHm, thanks š
@U06BE1L6T, Iāve been playing around in the repl to build a test, and wanted to transfer an expected result to a test case.
Do I have to do prn and use reader?
Can someone explain this :
(defn palindrome?
"Checks if a word is a palingdrome"
[word]
(= (seq word) (reverse word)))
(defn countPalingdromes
"count the number of palingdromes between 0 and 9999"
[]
(->> (range 0 9999) (filter palindrome?) (count)))
error message :
; Execution error (IllegalArgumentException) at ground-up.chapter5/palindrome? (form-init12029216532296771069.clj:41).
; Don't know how to create ISeq from: java.lang.Long
palindrome expects a sequence and it's being given a number. try:
(->> (range 0 9999) (map str) (filter palindrome?) (count))
Anyone know how to request with content-type image/png in reitit? I need to create upload image API. Multipart also won't works, i keep getting ClassNotFoundException javax.servlet.http.HttpServletRequest
(def logging-enabled :true)
(defmacro log [message]
(if (= logging-enabled :true)
'(prn message)
nil))
(macroexpand `(log :hi))
To define macros, you need to understand the difference between ⢠quote (ā apostrophe) ⢠syntax-quote (` back-tick) ⢠unquote (~ tilde) The syntax quote needs to be used, when you need to create an expression using a variableās value, not the variable itself, done by unquoting the variable. In your example you need to syntax quote the (prn ā¦) and unquote message. Also, you need to quote nil, since that is a return expression.
before I forget this is the challenge I try to solve:
; Write a macro log which uses a var, logging-enabled, to determine whether or
; not to print an expression to the console at compile time. If logging-enabled
; is false, (log :hi) should macroexpand to nil. If logging-enabled is true,
; (log :hi) should macroexpand to (prn :hi) .
Syntax error compiling at (chapter5.clj:62:1).
; Unable to resolve symbol: defmacro in this context
wierd, if i do crtl-enter
first on a defn
and then on the defmacro
it works, If I do tit direct on a defmacro
I see the above error
Hello Clojurians, I am trying to build subsequences from an original seq according to specific criteria (4Clojure p53, https://www.4clojure.com/problem/53#prob-title). I am trying to implement it via a recursive function, any suggestions for a higher level approach? Thanks in advance.
@mzuppichin a code walk through of how I put together first a very low abstraction loop recur and then used partition for a higher level of abstraction https://github.com/practicalli/four-clojure/blob/master/src/four_clojure/053_longest_increasing_sub_seq.clj
There is also a video of this walk through here https://www.youtube.com/watch?v=pyIbP4BOGpQ&list=PLpr9V-R8ZxiDjyU7cQYWOEFBDR1t7t0wv&index=36
@jr0cket Thanks sincerely, I have tons of material to study now!
Thanks for the quick and exhaustive response!
If you can find any other solutions, do let me know š
any expert who can give feedback on my code : https://github.com/RoelofWobben/Learning_in_public/blob/main/ground_up/src/ground_up/chapter5.clj
The namespace in the file should use a dash instead of an underscore
(ns ground-up.chapter5
Ā (:import [java.time LocalTime]))
However, the file name should remain as an underscore, as the Java virtual machine that this Clojure code runs on does not support dashes in file names. I appreciate this is awkward at first.I am unaware of the problem, I havent looked into the history. However the ns definition I suggest is the preferred syntax.
@U051B9FU1 how do you mean that ?
I am unclear why macros are used. They are a small part of writing clojure applications and require more of a learning curve if you are just beginning. Not something I can help with, sorry.
(def my-schedule
[{:activity "brush teeth" :time "7"}
{:activity "gel hair" :time "7.15"}])
for example. then you can have the same fn work on different schedules. and you can validate them to see if they make any sense
@U051B9FU1 I think that I understood what you mean but I also think that is more then I can chew now. Writing clojure now for 3 - 4 days
Do any of you know if the spit function reads entire file content when using the append true parameter?
Confusing this :
user=> (def x (future (prn "hi") (+ 1 2)))
"hi"
#'user/x
user=> (deref x)
3
is there a function build-in which reverses the args of a binary function. i.e. which maps f
to (fn [a b] (f b a))
?
of course I can write this but it is one of those functions like comp
which is useful when doing functional manipulation
I need this most often when using condp
to reverse the args of the predicate
I need to find the most frequent element of a sequence, and how many times it occurs. The way I'm doing it seems awkward.
(defn most-frequent [items]
(reduce (fn [[td c] [td' c']]
(if (< c' c)
[td c]
[td' c']))
[:empty-set 0]
(frequencies items)))
So (most-frequent '(a b a b c a b a b a a a d))
returns [a 7]
ok, sorting a map using sort-by val
is something i wouldn't have guessed. The code is concise, but I don't really need to sort, I just need to find the maximum element
perhaps something like max-key would be cleverer?
once I have the key, I can get the value, right?
if you just want the value
(apply max (vals (frequencies '(a b a b c a b a b a a a d))))
the doc for max-key suggests this recipe:
; find the key that has the highest value in a map
user=> (key (apply max-key val {:a 3 :b 7 :c 9}))
:c
so perhaps something like the following will give me the pair
(apply max-key val {:a 3 :b 7 :c 9})
max-key
is basically what my call to reduce
is doing, more or less.
cool. I'm glad I asked. thanks for the leads.
when using something like max, one must consider the cases of empty input.
for example my function dies when given an empty list.
I just posted this solution with max-key a few days ago on Twitter https://twitter.com/PrestanceDesign/status/1336726602777038849
max-key
is great!
When I first checked out the frequencies
source, I learned like 3 new things at once. And the function is only a couple of lines long
I think the documentation for condp
is wrong. can someone help me read it and figure out if I'm confused. The documentation says that the expression (pred test-expr expr)
is evaluated repeatedly, however it appears to me that expr
is only evaluated once, into a tmp-var and the expression that is evaluated repeatedly is (pred-test-expr tmp-var)
This is of course the desired behavior, but not the documented behavior.
I think that's what that doc is trying to convey
I agree that that is what's intended, and it is what the user would assume from NOT reading the doc. But when I read it carefully, I had to perform an experiment to verify.
if you're using a side effecting expr where you would see a different result between those two interpretations, then I would suggest maybe that's a bad idea
I'm writing a macro which expands to several invocations of condp
. I needed to know whether my macro needs to capture the value of the expression in a let
or whether condp
already does that for me.
as to whether it is a good idea to have side effects in the given expression, I understand your concern. But if I'm writing a macro where the user passes the expression to me, I don't have the privilege of requiring that there be no side effects.
sure you do - just say that's not allowed in the docs :)
Well I prefer that if the user gives me an expression with side effects, then those side effects are only effectuated a maximum of once. That is much friendlier for debugging.
And is in fact what condp
does, despite a biblical fundamentalist reading of the holy documentation text š grins
Here is an example of the macro, https://gitlab.lrde.epita.fr/jnewton/clojure-rte/-/blob/master/doc/genus.md#typecase if you're interested
Hello all, I'm trying to create a CSV using (https://clojure.github.io/data.csv/) , however I don't want to write to disk and would like the output to be a byte array. Because I'm unfamiliar with writer, I'm not sure how to do this.
(with-open [writer (io/writer file-path)]
(clojure.data.csv/write-csv data file-path))
How can I change this to return a byte array and not write to disk? Thanks
(let [bytes (java.io.ByteArrayOutputStream.)]
(with-open [writer (io/writer bytes)]
(csv/write-csv writer [["a" "b" "c"] [1 2 3] [4 5 6]]))
(str bytes))
Thanks. I changed the last part and got what I needed.
(defn data->byte-array [data]
(let [bytes (java.io.ByteArrayOutputStream.)]
(with-open [writer (io/writer data)]
(csv/write-csv writer data))
(.toByteArray bytes)))
(data->byte-array data) ;; [34, 91, 34, 34, 48 ...]
(type (data->byte-array data)) ;; [B
Thanks again!
I am working on rewrite-cljc docs and want to check my terminology usage. When I first came to Clojure I really had no concept āformā or āsexprā. Maybe I still need help. Can these terms be used interchangeably? Is the difference https://clojure.org/guides/learn/syntax#_structure_vs_semantics?
I know that a string in quotes is an expression, but I'm not sure whether a non-parenthesized piece of text is a form????
strings and numbers and symbols are s-expressions.
are they forms?
s-expression is either an atom (including strings and symbols) or an expression (x y ā¦)
where x and y are s-expressions
so literals like [1 2 3]
or {:x 1}
(vectors and maps) are not s-expressions.
s-expression is a subset of form in clojure terminology imho
Thanks @U010VP3UY9X and @U04V4KLKC, I guess Iāve tested the waters for strong opinions on this. I think Iāll just go ahead and describe the terms rewrite-clj already uses with the caveat they might not be absolutely technically correct.
Found this definition which seems to match rewrite-clj usage: https://www.braveclojure.com/do-things/#Forms
Confusing this :
user=> (def x (future (prn "hi") (+ 1 2)))
"hi"
#'user/x
user=> (deref x)
3
why does hi printed direct andĀ the adding later
why not both later ?future runs the expression in a different thread
concurrent to the main repl thread
(although they both share the same stdout which can be confusing)
from (doc future)
> Takes a body of expressions and yields a future object that will
> invoke the body in another thread, and will cache the result and
> return it on all subsequent calls to deref/@.
so "hi" is being printed in thread 2, then the deref returns the result of the expression and prints it in the main thread
oke, so printed cannot be deferred. If I would do there another calculation then both were deffered ?
a delayed expression is only run once you deref or force the delay (and happens on the same thread)
I expect nothing but try to understand what happens. It thought both are deffered but I see the output of `(prn "hi") direct and I try to understand why that one is direct executed and the calculation is deffered
there is no real ordering of output from another thread (the future) and the output from the repl process (prompting you for input)
so basically it is a race condition, how the output of the repl is interleaved with the output of the future
future
will evaluate everything in its body as soon as possible, in a separate thread, subject to operating system, thread scheduling, etc. constraints.
^^ future is not about future evaluation, it's about getting the result in the future
evaluation is immediate and concurrent
Clojure Applied has a great chapter on all this btw, also the author is quite strong and handsome
@roelof perhaps one source of your confusion is that some output in the repl is the implicit print of values as you ask for them to be calcuated, and other output is arbitrary printing behavior?
oke, so using a future
is not a good thing because you never know when the result is returned. And there im confused
the result is returned when you ask for it
any random prints are done as they are executed
this is why I was pointing out the distinction between things implicitly printed (because your REPL form returned them), and things explicitly printed (because you wrote code that prints)
the result of an expression, and output printed by an expression are not the same thing
> oke, so using aĀ futureĀ is not a good thing
a future
is a thing that could be exactly what you need or very inappropriate. Can you explain what you want to accomplish and the advice can be more tailored to that?
oke, lets say we have this : (def x (future (* 3 4 ) (+ 1 2)))
I see a message #'ground-up.chapter5/x
which is the 3
(do (+ 1 2) (+ 10 20))
will return 30
. the (+ 1 2)
is computed and ignored as its unused anywhere and not returned
this isn't just a property of future (which contains an implicit do), it also applies to other places that contain a do block, like let, fn, defmacro, doseq, loop...
all forms in the do block but the one you return are only useful as side effects
oke, I think I understand it but still I think a wierd example in the book to explain futures
it's showing that the execution happens immediately (via the print side effect), and you can get the value whenever you are ready for it
it seems like it really demonstrated what's going on. the println happened immediately and the value comes when derefed. Pretty good probing of how future runs immediately and caches its value for dereferencing
anyone more who want ot give feedback on my code.See question on the code-review chapter
Hi, I have this deps.edn
{:paths ["src" "resources"]
:deps {org.clojure/clojure {:mvn/version "1.10.1"}
aysylu/loom {:mvn/version "1.0.2"}
org.clojure/math.combinatorics {:mvn/version "0.1.6"}}
:aliases
{:remote-repl {:extra-deps {vlaaad/remote-repl {:mvn/version "1.1"}}}
:reveal {:extra-deps {vlaaad/reveal {:mvn/version "1.0.130"}}}
:test {:extra-paths ["test"]
:extra-deps {org.clojure/test.check {:mvn/version "1.0.0"}}}
:runner
{:extra-deps {com.cognitect/test-runner
{:git/url ""
:sha "b6b3193fcc42659d7e46ecd1884a228993441182"}}
:main-opts ["-m" "cognitect.test-runner"
"-d" "test"]}
:uberjar {:extra-deps {seancorfield/depstar {:mvn/version "1.1.128"}}
:main-opts ["-m" "hf.depstar.uberjar" "aoc.jar"
"-C" "-m" "stuartstein777.aoc"]}}}
And this .clj file:
(ns stuartstein777.2020.day10
(:require [clojure.string :as str]
[clojure.math.combinatorics :as combo]))
WHen I try to run anything in the repl in this namespace I just get the error:
Execution error (FileNotFoundException) at stuartstein777.2020.day10/eval1477$loading (day10.clj:1).
Could not locate clojure/math/combinatorics__init.class, clojure/math/combinatorics.clj or clojure/math/combinatorics.cljc on classpath.
I'm using INtelliJ, after recent update I get warnings about old project types and it asks to convert them. Now stuff that worked pre-update is broken.
Any ideas?Cant run REVL either, and this also worked until yesterday. Now it says cant run REVL, no project file found.
it sounds like something in IntelliJ that needs fixing, not clj
yeah, i just tried another project that 100% worked, and woudl run REVL too. Now it doesn't work either.
running clj in a terminal, then using require
to load your namespace should still work (or at least get further than not seeing a dep that's in your config)
Hi Everyone, I am a Javascript developer, I recently gotten introduced to Clojure world by a ReactĀ ClojureScript application. Are there any good resources you would recommend that wouldĀ will help me with this transition?
@singhamritpal49 If you're using Reagent, this might be a nice one: https://www.learnreagent.com/
@singhamritpal49 There's also lots of resources listed here: https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f
Hmm.. the free version of that course never loads for me.. I just get loads of errors in the js console
Maybe pass that as feedback to the author. It's kind of ironic that a webdev course doesn't work in all browsers.
yes, that's the same thing I thought š I already wrote about this problem in the chat that popped up
Hi! This is a good challenge for Clojure: https://twitter.com/wesbos/status/1337453660482187268 I'm sure there are a clean and simple way to solved this. What functions do you think are appropriate in order to achieve the results?
A library was used, but I like the R version š https://twitter.com/matthewpaulking/status/1337461670243799040
I have looked into the question and I am thinking of using merge-with, reduce, etc.
(let [col [["name" "id" "height"] ["Bob" "2" "50"] ["John" "1" "45"]]]
(map #(zipmap (map keyword (first col)) %) (rest col)))
({:name "Bob", :id "2", :height "50"}
{:name "John", :id "1", :height "45"})
Thx @dpsutton for the clue! This is my first iteration, what do you think? There is a more idiomatic way to get the result? https://paiza.io/projects/9-Daq1Zrz1s-al0nqE0YUA?language=clojure
(ns challenge
(:require [clojure.pprint :as pp]))
(def arr1 [["name", "id", "age", "weight", "Cool"]
["Susan", "3", "20", "120", true]
["John", "1", "21", "150", true]
["Bob", "2", "23", "90", false]
["Ben", "4", "20", "100", true]])
(def arr2 [["name", "id", "height"]
["Bob", "2", "50"]
["John", "1", "45"]
["Ben", "4", "43"]
["Susan", "3", "48"]])
(def arr3 [["name", "id", "parent"]
["Bob", "2", "yes"]
["John", "1", "yes"]])
(defn parse-array [col]
(map #(zipmap (map keyword (first col)) %) (rest col)))
(defn merge-array [arr]
(apply concat (map parse-array arr)))
(def result (map #(apply merge (val %)) (group-by :name (merge-array [arr1 arr2 arr3]))))
(pp/print-table [:id :name :age :weight :height :Cool :parent]
result)
@UFBL6R4P3 Shouldnāt it be group-by :id
instead of group-by :name
?
I suggest to avoid the shortcut for the anonymous function, so that you can name the entry param. It helps the code staying readable.
Another suggestion is to use ->>
, it helps reading the transformations as they occur chronologically.
parse-array
is fine
If you use my extended version of group-by
, you can have something even shorter and easier to read:
(def result
(->> [arr1 arr2 arr3]
(map parse-array)
(apply concat)
(group-by :id identity merge)
vals))
The source code of my group-by
is:
(defn group-by
"Same as clojure.core/group-by, but with some handy new arities which apply
custom map & reduce operations to the elements grouped together under the same key."
([kf coll]
;(group-by kf identity conj [] coll)
(cc/group-by kf coll))
([kf vf coll]
(group-by kf vf conj [] coll))
([kf vf rf coll]
(group-by kf vf rf (rf) coll))
([kf vf rf init coll]
(->> coll
(reduce (fn [ret x]
(let [k (kf x)
v (vf x)]
(assoc! ret k (rf (get ret k init) v))))
(transient {}))
persistent!)))
For more info on this group-by: https://twitter.com/VincentCantin/status/1337815470469042176
Thx a lot @U8MJBRSR5!
Iām doing well with āsimpleā recursion, like with loop/recur or without. But what I am struggling with is the case when I have to recur into a list of things ⦠like when generating a tree or explore multiple paths
I have gotten into the habit of starting with something like an secd or cek machine whenever I need to process a tree, because those are abstract machines for evaluating programs, and programs are trees
the idea of storing continuations in a stack, processing a list of results and then doing something in the middle of a tree, etc are common features of tree processing and program evaluation
I can't claim it is good, I don't think the programs that come out of it are particularly readable
the last thing I did that with is a pretty printer for graphql schemas and it is very ugly
related to zippers are lenses, which there are a few clojure libraries for, or something like spectre
i think i basically need something higher level that allows me to give it a function to answer āwhat are the children for this nodeā repeatedly until i have no more data for that branch
if you need to edit nodes in a nested immutable data structure, zippers are awesome. for simply walking a nested data structure, tree-seq
can be an easier alternative
Thx @dpsutton for the clue! This is my first iteration, what do you think? There is a more idiomatic way to get the result? https://paiza.io/projects/9-Daq1Zrz1s-al0nqE0YUA?language=clojure
(ns challenge
(:require [clojure.pprint :as pp]))
(def arr1 [["name", "id", "age", "weight", "Cool"]
["Susan", "3", "20", "120", true]
["John", "1", "21", "150", true]
["Bob", "2", "23", "90", false]
["Ben", "4", "20", "100", true]])
(def arr2 [["name", "id", "height"]
["Bob", "2", "50"]
["John", "1", "45"]
["Ben", "4", "43"]
["Susan", "3", "48"]])
(def arr3 [["name", "id", "parent"]
["Bob", "2", "yes"]
["John", "1", "yes"]])
(defn parse-array [col]
(map #(zipmap (map keyword (first col)) %) (rest col)))
(defn merge-array [arr]
(apply concat (map parse-array arr)))
(def result (map #(apply merge (val %)) (group-by :name (merge-array [arr1 arr2 arr3]))))
(pp/print-table [:id :name :age :weight :height :Cool :parent]
result)