beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
2020-11-24T00:37:23.289700Z

Patterns of what?

2020-11-24T00:39:05.292200Z

Spec has regex ops for recognizing patterns of data structures, the implementation of those most closely matches taking the derivative of the regular language, which is a nice simple method for implementing regular expressions bit doesn't get used much

2020-11-24T00:40:45.294Z

I believe https://github.com/ztellman/automat uses the traditional NFA transformation to recognize sequences of values

👍 1
2020-11-24T00:42:30.295800Z

There is another library whose name escapes me that is more deliberately regexes on top of clojure sequences, I don't know what the internals of that are like

2020-11-24T00:43:09.296700Z

If you just need to implement regexes, the derivative route is way easier

okwori 2020-11-24T00:50:12.296800Z

Strings

2020-11-24T01:07:03.297700Z

if you have a java lib you can just use it

zach 2020-11-24T01:23:52.301Z

Hi! I am working to understand clojure.spec on a project and hoping to find a bit of illumination on the exercise-fn and the test/check functions. Just as a toy example, I wrote a function that takes an email and returns a string.

(defn toy-fn [email] (str "the email is " email))

zach 2020-11-24T01:24:45.301800Z

I then wrote a spec for email as

(s/def ::email (s/and string? #(re-matches email-regex %))

zach 2020-11-24T01:25:28.302500Z

and I wrote a function spec as

(s/fdef toy-fn
:args (s/cat :email ::email)
:ret string?)

zach 2020-11-24T01:26:35.303500Z

When I do either a (test/check toy-fn) or (s/exercise-fn toy-fn) I get an error of: Couldn’t satisfy such-that predicate after 100 tries

seancorfield 2020-11-24T01:26:52.304100Z

You'll need a custom generator on your ::email Spec.

zach 2020-11-24T01:27:04.304500Z

I read in this spec faq (https://blog.taylorwood.io/2018/10/15/clojure-spec-faq.html) that this is because it’s trying to match just for string

seancorfield 2020-11-24T01:27:30.305600Z

The problem is that string? generates a very broad range of random strings and the vast majority will not satisfy your regex predicate.

zach 2020-11-24T01:28:15.306800Z

right right! I was wondering what would be the idiomatic way of doing this, or a suggested way of doing this. Is it more common to write the generator as part o the spec, or as something I feed into a test function?

seancorfield 2020-11-24T01:28:55.308200Z

Gary Fredericks has a library called test.chuck that includes a regex generator which you could use to generator strings that would conform to your Spec. Or you could just generate some "canned" emails for testing (e.g., using a set of email addresses).

zach 2020-11-24T01:29:00.308400Z

I have a few specs like this, and some that are maps of these things (e.g. a map of a username and an email, with both fitting a regex)…and I was excited about writing less code with clojure.spec , but it seemed like i’d be writing custom generators for much of my specs…and wanted to check there wasn’t a better way.

seancorfield 2020-11-24T01:29:53.309100Z

If you write the generator as part of the Spec, then aggregates of those Specs should generate automatically.

seancorfield 2020-11-24T01:30:50.310300Z

Here's an example from our codebase at work:

(s/def ::email (s/with-gen (s/and (bounded-string 5 128)
                                  wstr/valid-email?)
                 (wgen/fn-string-from-regex wstr/email-regex)))

seancorfield 2020-11-24T01:31:41.311200Z

(defn fn-string-from-regex
  "Return a function that produces a generator for the given
  regular expression string."
  [regex]
  (fn []
    (let [string-from-regex (requiring-resolve 'com.gfredericks.test.chuck.generators/string-from-regex)]
      (string-from-regex regex))))
That's just done so we only depend on test.chuck at runtime, during testing.

seancorfield 2020-11-24T01:32:17.311600Z

and here's our email regex:

(def email-regex
  "Sophisticated regex for validating an email address."
  (re-pattern
   (str "(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|"
        "(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|"
        "(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))")))

zach 2020-11-24T01:32:31.311900Z

oh my gosh this is handy.

zach 2020-11-24T01:33:46.313Z

And because yr generator is defined as part of the spec, if you had (`test/check email)` and (`test/check coll-of-emails)`, you don’t have to feed sample emails into both tests.

seancorfield 2020-11-24T01:34:11.313800Z

It generates gnarly stuff:

user=> (s/exercise ::email 10)
(["\"𭏩\"@[991.8.76.94]" "\"𭏩\"@[991.8.76.94]"]
      ["\"񜑣𶤍\"@-a.JX" "\"񜑣𶤍\"@-a.JX"]
      ["𛴹.񓬝󾆜򆘔.񎔞@[0.2.306.04]" "𛴹.񓬝󾆜򆘔.񎔞@[0.2.306.04]"]
      ["󝖣󄂪@[5.293.2.240]" "󝖣󄂪@[5.293.2.240]"]
      ["򪤷.𵲥藮@[96.9.946.5]" "򪤷.𵲥藮@[96.9.946.5]"]
      ["𰔀򟡙񛼏�.󶰿񛧂򥙭󅟜.򲰴􌭨𭄁􏔛󗤠񡄃@E.NN6.erjjWW" "𰔀򟡙񛼏�.󶰿񛧂򥙭󅟜.򲰴􌭨𭄁􏔛󗤠񡄃@E.NN6.erjjWW"]
      ["\"􆓑𬸸򯐜򿳪񜺈讴񃪕\"@J.w0P.21x.hRxb" "\"􆓑𬸸򯐜򿳪񜺈讴񃪕\"@J.w0P.21x.hRxb"]
      ["񯸯.𓱨񷤖񕂉𥺼򇔵򡜕򞝹𸏙.󂶹.󿎛򨐪愸񱞶.󺖆򬃞󼾽󍎲.򴹧볿𲄆.󋲽𵛍򩠐󌡇򣛳󥊰򢍕󰅚.񊍁𲒣񊽫򘱌𨇪@[2.393.765.1]" "񯸯.𓱨񷤖񕂉𥺼򇔵򡜕򞝹𸏙.󂶹.󿎛򨐪愸񱞶.󺖆򬃞󼾽󍎲.򴹧볿𲄆.󋲽𵛍򩠐󌡇򣛳󥊰򢍕󰅚.񊍁𲒣񊽫򘱌𨇪@[2.393.765.1]"]
      ["󲶍򂇇򤍩󐞨󂁚񵚌.ⵜ񫱽𬀄򘖲󺫟򆳫鶆񗈡玉.񣰮󆪕�􋆉򦻼@l0u.C6d4qPVy.R3s.F3P2kkwd.br62Nkb4.aE-JqLl.bO.IKffxYXLW.gqyIcL" "󲶍򂇇򤍩󐞨󂁚񵚌.ⵜ񫱽𬀄򘖲󺫟򆳫鶆񗈡玉.񣰮󆪕�􋆉򦻼@l0u.C6d4qPVy.R3s.F3P2kkwd.br62Nkb4.aE-JqLl.bO.IKffxYXLW.gqyIcL"]
      ["\"񅸦򪅢댴򨱹𒊱򰍉\"@lzFjoe.aWJ9k.-9CxZ1b.TdUWCK.Nn.urDeD.FDpBg" "\"񅸦򪅢댴򨱹𒊱򰍉\"@lzFjoe.aWJ9k.-9CxZ1b.TdUWCK.Nn.urDeD.FDpBg"

seancorfield 2020-11-24T01:35:58.314300Z

Right, so

user=>  (s/def ::emails (s/coll-of ::email :kind vector?))
user=>  (s/exercise ::emails 3)
([["򸢧@[6.84.4.329]" "󐿎@[8.4.444.65]" "\"񳾈\"@[59.775.8.4]" "\"򒙊\"@5.Ub" "񂚝@L.Sq" "򒏙@[3.87.8.441]" "\"􆄅\"@u.fn" "󾟅@[618.644.46.084]" "\"񓥹\"@S.HM" "򎹉@[8.56.028.56]" "\"󖧩\"@[4.30.361.922]" "\"󕪤\"@w.WG" "\"𒨴\"@[307.508.705.640]" "\"󰡻\"@s.tq" "󙴏@M.pw" "�@I.tb" "\"񿴛\"@[04.9.3.2]" "\"񁽰\"@n.Eg" "\"􄚯\"@A.vm" "\"󆆻\"@[2.4.95.3]"]
       ["򸢧@[6.84.4.329]" "󐿎@[8.4.444.65]" "\"񳾈\"@[59.775.8.4]" "\"򒙊\"@5.Ub" "񂚝@L.Sq" "򒏙@[3.87.8.441]" "\"􆄅\"@u.fn" "󾟅@[618.644.46.084]" "\"񓥹\"@S.HM" "򎹉@[8.56.028.56]" "\"󖧩\"@[4.30.361.922]" "\"󕪤\"@w.WG" "\"𒨴\"@[307.508.705.640]" "\"󰡻\"@s.tq" "󙴏@M.pw" "�@I.tb" "\"񿴛\"@[04.9.3.2]" "\"񁽰\"@n.Eg" "\"􄚯\"@A.vm" "\"󆆻\"@[2.4.95.3]"]]
      [["󘖺.�񟑀@DC.fsX" "\"񐦲\"@[27.378.381.633]" "\"񪵐񺛕\"@Y.Sv.Hsf" "\"󱽠򚌳\"@[02.73.791.23]" "\"󤗭󱥀\"@[24.9.7.46]" "񅏪󆯊@[093.6.1.4]" "\"𹁮\"@[8.31.839.0]" "\"󺭵𵫎\"@E.dIk" "\"򚎾򆳈\"@[3.751.87.647]" "\"򝁾\"@5.wdM"]
       ["󘖺.�񟑀@DC.fsX" "\"񐦲\"@[27.378.381.633]" "\"񪵐񺛕\"@Y.Sv.Hsf" "\"󱽠򚌳\"@[02.73.791.23]" "\"󤗭󱥀\"@[24.9.7.46]" "񅏪󆯊@[093.6.1.4]" "\"𹁮\"@[8.31.839.0]" "\"󺭵𵫎\"@E.dIk" "\"򚎾򆳈\"@[3.751.87.647]" "\"򝁾\"@5.wdM"]]
      [["\"󜟞򅿅󓄹\"@o-.QQfc" "\"񟮻쇪򾆯\"@aUe.I.Db.FmjY" "񐭿.򻺩򰩠@[37.4.6.976]" "񸯛񷊾󌹃@uT.6aZ.fsX" "\"󣅼\"@YJ0.uh" "\"񼈙򬎈\"@z.j.DNle" "\"򓢅򷳮\"@J.RsE" "\"񴌜\"@ZUW.owBf" "\"񜞯񿴨򁯵\"@u.U.USjW" "򊂮.𐊹𠱣𡷗.񏂶@lrw.zqn.gN" "𫸽񽅆򱕞@[31.9.15.13]" "𱌼󋥌.򏙘�񌺊@[00.441.127.31]" "󪫮.񭞪򪟝@m.E5U.Ab.bfy"]
       ["\"󜟞򅿅󓄹\"@o-.QQfc" "\"񟮻쇪򾆯\"@aUe.I.Db.FmjY" "񐭿.򻺩򰩠@[37.4.6.976]" "񸯛񷊾󌹃@uT.6aZ.fsX"...

seancorfield 2020-11-24T01:36:42.315200Z

Sometimes you may want to override a generator for a specific test case -- you might want readable test data, for example.

zach 2020-11-24T01:37:47.315900Z

That makes sense. I read a mention of overriding in the blog I linked above, but I couldn’t find an example of this in the spec guide.

zach 2020-11-24T01:38:43.316900Z

it said to feed a map to test/check…would it be something like (test/check email-fn {:gen [my list of emails]})

seancorfield 2020-11-24T01:39:46.317800Z

:gen takes a hash map from symbols to generators so it's a bit more involved than that

seancorfield 2020-11-24T01:40:11.318300Z

(assuming you mean clojure.spec.test.alpha/check there)

👍 1
seancorfield 2020-11-24T01:40:39.318500Z

:gen map from spec names to generator overrides

zach 2020-11-24T01:41:53.319Z

gotcha. Thank you!

0xclj 2020-11-24T02:08:18.319400Z

@tskardal, that gives the result. Thanks!

0xclj 2020-11-24T02:10:50.319600Z

@simongray, @denis.baudinot Ordering is not a concern. Can you help with an example on how I’d go about using a set to achieve the result? Thanks!

0xclj 2020-11-24T02:17:11.319800Z

@sova, I’m not sure how I’d go about the structure you mention to achieve the resulting structure. The base-collection in the example is from an API response. Are you suggesting I transform the response to the structure you mention, and that should make the extraction of emails /phones simpler?

0xclj 2020-11-24T02:22:43.320Z

@alexmiller, Didn’t think of it that way. I can see the parallels now.

Emil 2020-11-24T12:36:52.323100Z

I'm trying to figure out how one would go about executing cljs builds using clojure tools/deps.edn without shadow, lein etc. Something like this (I'm aware of the fact that you can run cljs.main with :main-opts set)

{:paths ["src"]
 
 :deps 
 {org.clojure/clojurescript {:mvn/version "1.10.758"}}


 :aliases
 {:build/release 
  {:exec-fn cljs.build.api/build
   :exec-args {:src "src" :opts {:optimizations :advanced} } }}}

Jonas Claesson 2020-11-24T13:44:36.326300Z

I'm trying to write a generative test, but get an exception "Wrong number of args (10) passed to: jobtech-taxonomy-api.test.generative-test/send-concept-request" when running (spec/exercise-fn `send-concept-request) in the repl. I want to debug why this has happend, but I can't find a way to inspect the exception stack. I can see the stack with *e, but not the values passed between each frame. In C I would have written 'bt full' in gdb to get the full stack. But how to you do that in Clojure? I want to see the 10 arguments, and how they were generated and passed down.

Jonas Claesson 2020-11-25T16:17:25.395300Z

Thanks for the answer. Too bad this info is not available, I have needed it a few times. But I'll try the wrapping instead.

2020-11-25T16:23:20.395900Z

also if you use the cursive plugin with intellij, you'll have more native java integration features (I find the IDE paradigm obnoxious so I don't use it, but ymmv)

2020-11-25T17:02:06.396300Z

you might want to check out this API - I haven't tried it myself but it seems to provide what you are looking for https://docs.oracle.com/en/java/javase/11/docs/api/jdk.jdi/com/sun/jdi/StackFrame.html

st3fan 2020-11-24T14:18:46.326900Z

Hm fun one .. I start a message queue worker in a (def) and I think that is why lein uberjar is hanging

st3fan 2020-11-25T19:38:34.398500Z

a beginner i am

Gabriel Augusto Reis da Silva 2020-11-24T14:50:35.328200Z

I'm trying to make tests in my API for study, using nubank matcher combinators. But, in my "post" route test the output show this error:

java.lang.Exception: JSON error (unexpected character): N
Could anyone help me please? 😉

Darin Douglass 2020-11-24T15:04:48.329100Z

first thought is that N is the beginning of Not found or something that's not json

✅ 1
jstaab 2020-11-24T15:05:24.329500Z

Check your payload, that's an error you'll get from json/read-str

✅ 1
jstaab 2020-11-24T15:05:35.330Z

(require 'clojure.data.json)
nil
user=> (clojure.data.json/read-str "N")
Execution error at clojure.data.json/-read (json.clj:230).
JSON error (unexpected character): N

2020-11-24T16:33:26.331300Z

I'd start with figuring out the java command that invokes the cljs compiler in the way you want, then translate to deps.edn config. Also there are features that shadow provides beyond compiling your clojurescript, and it can be used with deps.edn.

2020-11-24T16:36:55.333100Z

The jvm stack trace object doesn't carry that data, sadly. The jdb command has many of the features of gdb, but will also require being familiar with the constructs that clojure bytecode creates. Alternatively you can easily wrap send-concept-request so that it captures its args for debugging.

2020-11-24T16:37:33.333800Z

right, any side effects in namespaces are invoked when you aot compile

2020-11-24T16:37:49.334200Z

this is a very common trap for beginners to fall into

MatthewLisp 2020-11-24T16:37:54.334400Z

Hello Clojurists 😄 What's the easiest way to achieve simple parallelism with clojure? By that i mean, running two functions together at the same time. Someone recommended me to use promise but i can't see how this is achieved through promises, because when i try to deref the first, it will block until it's computation is done, and only then proceed to the next one.

2020-11-24T16:38:35.334700Z

promise doesn't do parallelism

2020-11-24T16:38:49.335Z

future does

MatthewLisp 2020-11-24T16:40:51.335600Z

oh, right, reading the docs, makes sense

2020-11-24T16:41:28.336200Z

perhaps they were thinking of js, which does have promises but no parallelism

Gabriel Augusto Reis da Silva 2020-11-24T16:45:41.337Z

Thanks, I thought it was just that

Gabriel Augusto Reis da Silva 2020-11-24T16:46:15.337200Z

Thanks, you helped me a lot

MatthewLisp 2020-11-24T16:59:15.341100Z

i have a very newbie question, because i don't have much knowledge about parallelism and so on. But for example:

(ns user)

(require '[throttler.core :refer [throttle-chan throttle-fn]])

(def +# (throttle-fn + 100 :second))

(let [p1 (promise)
      p2 (promise)
      p3 (promise)]
  (deliver p1 (time (dotimes [_ 300] (+# 1 1))))
  (deliver p2 (time (dotimes [_ 300] (+# 1 1))))
  (deliver p3 (time (dotimes [_ 300] (+# 1 1)))))

;; "Elapsed time: 3044.38462 msecs"
;; "Elapsed time: 3079.034443 msecs"
;; "Elapsed time: 3075.497436 msecs"

(let [p1 (future (time (dotimes [_ 300] (+# 1 1))))
      p2 (future (time (dotimes [_ 300] (+# 1 1))))
      p3 (future (time (dotimes [_ 300] (+# 1 1))))])

;; "Elapsed time: 9145.961132 msecs"
;; "Elapsed time: 9186.730517 msecs"
;; "Elapsed time: 9196.688568 msecs"
I'm using a library for throttling functions (changing throughput rate) just to analyse the time results. On the first approach, where i use promises (that DOESN't achieve parallelism), i can see that the total time of execution is roughly 9 seconds. On the second approach i use futures (that DOES achieve parallelism) and the total execution time is also roughly 9 seconds. What have i really achieved in terms of parallelism if the total time is the same as when doing it synchronously?

2020-11-24T17:00:13.341400Z

the timing is happening in parallel though

2020-11-24T17:01:35.342700Z

sorry, I misread for a moment

2020-11-24T17:01:43.343Z

this is a classic problem with parallelism

2020-11-24T17:02:57.343500Z

so that throttling - is it being applied per invocation, or globally?

MatthewLisp 2020-11-24T17:03:36.344100Z

i think it's per invocation, if per invocation means that it's applied when you call the function

2020-11-24T17:04:09.344700Z

I mean does it limit "maximum throughput for this function, across all call sites" or "maximum throughput at this call"

2020-11-24T17:04:19.345Z

because those numbers make it look like the former

2020-11-24T17:05:50.345700Z

@matthewlisp yeah, looking at the project, it looks like what this lib does is slow down your function globally, making parallelism pointless https://github.com/brunoV/throttler

2020-11-24T17:06:21.346300Z

it creates a single "spigot" controlling throughput of your function across all calls

2020-11-24T17:07:28.347100Z

try just using (defn slow-plus [& args] (Thread/sleep 1000) (apply + args))

2020-11-24T17:07:54.347700Z

that sleeps 1000 ms on each invocation, but the slowdown is per call, not globally, so parallelism will actually do something

2020-11-24T17:11:37.348Z

@matthewlisp

(cmd)user=> (time (let [a (promise) b (promise) c (promise)] (doseq [p [a b c]] (deliver p (slow-plus 1 1)))))
"Elapsed time: 3000.869278 msecs"
nil
(cmd)user=> (time (let [a (future (slow-plus 1 1)) b (future (slow-plus 1 1)) c (future (slow-plus 1 1))] (doseq [f [a b c]] (deref f))))
"Elapsed time: 1000.468 msecs"
nil

2020-11-24T17:12:36.348700Z

that throttler lib does expose an important point though: if your bottle neck is a global resource, rather than CPU, parallelism won't help

2020-11-24T17:13:49.349600Z

also, this usage of promises is weird - they are quite useful if you need to coordinate between threads and don't need the full complexity of core.async, but here they are doing nothing

MatthewLisp 2020-11-24T17:18:18.349800Z

ohhh right, i got it

MatthewLisp 2020-11-24T17:18:27.350Z

thanks for the help

clojuregeek 2020-11-24T17:25:02.352900Z

I'm using deps.edn, and clojure reports 1.10.1 when i start the repl. Yet on my mac it accepts -m to run my tasks... and linux (also reporting same clj version) it has to use -a ... why does it report the same version but yet have these minor differences?

alexmiller 2020-11-24T17:26:01.353600Z

1.10.1 is the Clojure (language) version. if you do clj -Sdescribe you can see the version of your clojure tools

alexmiller 2020-11-24T17:26:48.354600Z

any version of the Clojure tools can use any version of Clojure (forward and backward) but they share a number because that's the default you'll get if you don't specify (and the version the tools themselves are using)

alexmiller 2020-11-24T17:27:19.354900Z

I assume your linux version is older

clojuregeek 2020-11-24T17:28:37.355300Z

ok thanks, i'll check the tool version

seancorfield 2020-11-24T18:06:42.356800Z

@clojuregeek The change in behavior (around -M / -A -- uppercase) happened in 1.10.1.697 in the stable releases of the tools per: https://clojure.org/releases/tools (there were a whole bunch of prerelease versions between 1.10.1.561 and that).

seancorfield 2020-11-24T18:08:12.358100Z

I ended up using linuxbrew for installing the Clojure CLI on Linux so that i can brew upgrade clojure/tools/clojure on both macOS and Linux to keep up with the latest versions.

seancorfield 2020-11-24T18:09:02.359300Z

(well, if I was on stable, that's what I'd do -- I actually use brew install <mailto:clojure/tools/clojure@1.10.1.xxx|clojure/tools/clojure@1.10.1.xxx> to install specific versions since I'm always running the prerelease builds)

clojuregeek 2020-11-24T18:10:33.360300Z

Thanks, i'll look at Linxu brew. I was installing clojure via instructions on Eric Normands site https://purelyfunctional.tv/guide/how-to-install-clojure/#linux ... but Linux Brew might be better solution here 🙂

seancorfield 2020-11-24T18:14:22.361300Z

I switch between macOS and Ubuntu (WSL2 on Windows) all the time so I just find it so much easier to be able to use brew everywhere and not have to think about which platform I'm one! 🙂

👍 2
juniusfree 2020-11-24T22:42:52.363700Z

@jr0cket 😲 I saw this before when I'm searching for repl. Do you have a twitter account?

2020-11-24T23:49:37.369Z

HI, I'm trying to solve this problem on codewars in Clojure https://www.codewars.com/kata/52a78825cdfc2cfc87000005/train/clojure I have the tests almost passing but I'm failing on things like

Test Failed
expected: (= (sol/calc t2) (calc t2))
  actual: (not (= 1.1818181 1.1818181818181819))
When I parse out the numbers I'm using (Float/parseFloat). Does that test make it look like I should be using a different number type?

dgb23 2020-11-24T23:49:48.369400Z

There are several specs and spec arguments (primitives ?) that take concrete values, such as s/int-in and :min-count etc. Is it weird/wrong to provide these values via a binding and create specs dynamically for validation or parsing (with conform). For example it seems useful to read them from a database and then register a spec based on that. A slightly more involved example would be to create a spec for a map or tuple where one value is dependant on the other.

2020-11-24T23:54:38.369700Z

1.0 is a double literal in clojure, not a float literal, I'd use Double/parseDouble

seancorfield 2020-11-24T23:58:01.370600Z

@denis.baudinot I wouldn't say it is weird or wrong, but it is hard with Spec 1. It's much easier with Spec 2 (but that's not ready for production use yet).