beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
2021-05-12T00:03:46.057400Z

What about imagez? https://github.com/mikera/imagez It might be a bit heavy for this though, but it lets you get image width and height. Here's a quick example:

(defn get-image-properties [path file]
  (let [img (mimg/load-image (str path "//" file))]
    {:measuremnt {:width (mimg/width img)
                  :height (mimg/height img)}}))

(let [path "//home/stuart//Pictures"
      files (->> (.list (io/file path))
                 (filter #(str/ends-with? % ".png")))]
  {:images (zipmap (map keyword files)
                   (map (partial get-image-properties path) files))})
This outputs:
{:images
 {:Selection_023.png {:measuremnt {:width 842, :height 412}},
  :Selection_025.png {:measuremnt {:width 600, :height 426}},
  :Selection_020.png {:measuremnt {:width 850, :height 340}},
  :Selection_024.png {:measuremnt {:width 602, :height 426}},
  :Selection_021.png {:measuremnt {:width 1292, :height 965}},
  :Screenshot from 2021-04-12 00-22-05.png {:measuremnt {:width 857, :height 335}},
  :Selection_026.png {:measuremnt {:width 1000, :height 426}},
  :puzzle-storm-33.png {:measuremnt {:width 850, :height 340}}}}

2021-05-12T00:28:33.057700Z

Looks like you have a couple of dependencies set up here. What’s in your namespace?

2021-05-12T00:37:57.057900Z

(ns reference.core
  (:gen-class)
  (:require [clojure.string :as str]
            [<http://clojure.java.io|clojure.java.io> :as io]
            [mikera.image.core :as mimg]))

2021-05-12T00:38:23.058100Z

With this as a dependency in my project.clj

[net.mikera/imagez "0.12.0"]

2021-05-12T00:56:31.058300Z

Thanks! I’m a little confused what’s going on with (str path "//" file) and the (let [path "//home/stuart//Pictures" would you mind explaining?

2021-05-12T01:00:12.058500Z

Yeah I guess also just what are you putting in the path and file arguments?

2021-05-12T01:15:10.058800Z

File is just something like "foo.png" as that io file path call only returns the filename, not the path and file name. Imagez needs full path to file so that str is just taking folder where images is and appending the filename on. Needs the // to escape a /.

Franco Gasperino 2021-05-12T03:00:54.062300Z

How do i model a spec def to validate the value of an optional key in a map? Assuming the :c keyword and associated value are intended to be optional:

(spec/def ::acceptable-value? 
    (spec/and int? pos-int?))
  
  (spec/def ::a? ::acceptable-value?)
  (spec/def ::b? ::acceptable-value?)
  (spec/def ::c? ::acceptable-value?)

  (spec/def ::a ::a?)
  (spec/def ::b ::b?)
  (spec/def ::c ::c?)

  (spec/def ::acceptable-map? 
    (spec/and 
     (spec/map-of keyword? ::acceptable-value?)
     (spec/keys :req-un [::a ::b ::c])))

  (def my-map {:a 1 :b 2})
  (spec/valid? ::acceptable-map? my-map) =&gt; false
  (spec/explain ::acceptable-map? my-map) =&gt; {:a 1, :b 2} - failed: (contains? % :c) spec: ...

dpsutton 2021-05-12T03:07:43.062500Z

(require '[clojure.spec.alpha :as s])
(s/def ::c pos-int?)
(s/valid? (s/keys :opt-un [::c]) {}) ;; true
(s/valid? (s/keys :opt-un [::c]) {:c 3}) ;; true
(s/valid? (s/keys :opt-un [::c]) {:c "3"})  ;; false

dpsutton 2021-05-12T03:08:04.062800Z

the secret sauce you were missing was :opt-un

dpsutton 2021-05-12T03:10:14.063300Z

the docstring of s/keys didn't have quite the information i was expecting. > Creates and returns a map validating spec. :req and :opt are both > vectors of namespaced-qualified keywords. The validator will ensure > the :req keys are present. The :opt keys serve as documentation and > may be used by the generator.

Franco Gasperino 2021-05-12T03:12:42.063700Z

thank you

Franco Gasperino 2021-05-12T03:13:44.064Z

that's the secret sauce. works as expected.

2021-05-12T05:51:12.064100Z

tx for confirmation!

2021-05-12T06:22:21.066100Z

I am sorry if I should ask this on ask.Clojure or not, but what would be the potential disadvantage of using clojure.core.match, except for the potential slower speed?

dpsutton 2021-05-12T06:25:12.067900Z

two things i know of. 1) i believe it uses exception throwing as part of its backtracking. i thought i remembered reading somewhere that creating lots of exceptions can have some cost to it. 2) it's susceptible to creating code that is too large for a single method on jvms. The bad part of this is that it rears its head when you've gone down that path and add a few more cases and can be tricky because there's not much you can do

2021-05-12T11:39:39.070700Z

I have a vector of tuples, like so:

[["foo" 1] ["bar" 1] ["quax" 1] ["king" 2] ["queen" 3] ["pawn..." 6]]
Ordererd by the second item. I want to pull out all the items with the smallest second item. So in this case
[["foo" 1] ["bar" 1] ["quax" 1]]
Right now I'm doing something like
(let [smallest (second (first results))]
  (filter #(= (second %) smallest) results))
Is their a nicer way?

2021-05-12T11:43:53.071800Z

Oh, this seems better

(first (partition-by second [["foo" 1] ["bar" 1] ["quax" 1] ["king" 2] ["queen" 3] ["pawn..." 6]]))
=&gt; (["foo" 1] ["bar" 1] ["quax" 1])
The list is always going to small, only couple of dozen items.

alexmiller 2021-05-12T12:08:28.072300Z

sort-by, then partition-by ?

👍 1
Ivan Koz 2021-05-12T14:17:06.076300Z

In a recent article by Rich about history of clojure, there was two examples of lazy-seq provided, the last one line-seq doesn't wrap the first result "line" into LazySeq. Is that a design choice or just a matter of style? https://gyazo.com/09011955983e53357d340672b590aa01.png

alexmiller 2021-05-12T14:28:50.076600Z

I don't understand the question

alexmiller 2021-05-12T14:29:20.076800Z

(that is the actual implementation of line-seq btw)

Ivan Koz 2021-05-12T14:30:06.077Z

interleave-2 wraps first result in LazySeq, line-seq doesn't, the question is why

2021-05-12T14:31:57.077200Z

As in, could interleave-2 also work if its call to lazy-seq were just before the recursive call to itself?

2021-05-12T14:32:21.077400Z

that appears to be one way one could create a modified version of interleave-2 that would be implemented more like line-seq is.

2021-05-12T14:32:54.077600Z

My first impression is that for some reason that I don't yet understand, interleave-2 is "one step lazier" than line-seq.

alexmiller 2021-05-12T14:33:42.077800Z

(note that the self call is not recursive btw - macro expansion is happening here in a loop, no stack frames are harmed)

2021-05-12T14:34:51.078Z

Not sure what you want to use to name those calls -- what you would normally name a recursive call -- a call to the function currently being defined?

Ivan Koz 2021-05-12T14:35:41.078200Z

yes why not do (cons (first s1) (cons (first s2) (lazy-seq (interleave-2 (rest....))))

alexmiller 2021-05-12T14:36:01.078400Z

I think it's to make the result more lazy

alexmiller 2021-05-12T14:36:39.078600Z

I think in the line-seq case, you have to readLine so you've effectively already forced the head, so might as well reify

2021-05-12T14:39:30.078900Z

@nxtk I have not tested this, but you could probably get a function that behaves nearly identically to line-seq if you modified it by moving the lazy-seq call to wrap the entire body. That function would be "one step more lazy" than current line-seq, and more like interleave-2. If it works correctly, then it seems like a minor difference perhaps for no deep reason.

Ivan Koz 2021-05-12T14:39:34.079100Z

"you have to readLine" am i missing something? One could delay reading the first line and wrap read in LazySeq as in the interleave-2 case?

Ivan Koz 2021-05-12T14:41:37.079600Z

@andy.fingerhut yes, that was my question, kinda. Looks like there is no strict reason why line-seq couldn't be more lazy.

2021-05-12T14:41:49.079800Z

"no deep reason" approximately meaning "if Rich found a performance issue with his use of line-seq where it was doing the first readLine method call every time you called it, and he wanted to optimize for a case where he called line-seq on many files and never realized their first lines, he would probably change line-seq"

alexmiller 2021-05-12T14:41:51.080Z

I don't know, maybe it would work - the things I'd want to look at are termination, empty file case, and where you discover errors

2021-05-12T14:42:21.080200Z

"no strict reason" 🙂

alexmiller 2021-05-12T14:42:46.080400Z

(like what's the behavior if the file isn't readable)

alexmiller 2021-05-12T14:43:38.080600Z

it's probably preferable to have that exception throw on the call to line-seq rather than on the realization of the seq resulting from it

2021-05-12T14:43:40.080800Z

Note: My comments are ignoring backwards compatibility issues on cases like Alex is mentioning, which is also important now that line-seq has been implemented in the current way for N years.

Ivan Koz 2021-05-12T14:44:50.081Z

right, failing fast sounds reasonable

Ivan Koz 2021-05-12T14:46:49.081200Z

thank you guys

2021-05-12T14:47:24.081400Z

no problem. "Why" is one of the most important and interesting questions to ask, but in many cases also the most difficult to answer accurately.

Ivan Koz 2021-05-12T14:48:34.081600Z

bad, bad docs, undocumented semantics =)

2021-05-12T14:51:00.081900Z

Sometimes I think the view of the Clojure maintainers is "says no more than it needs to say, and every word is significant. Don't tie one's hands unnecessarily by the documentation in ways that prevents implementation changes in the future." I know that this is at odds with the kinds of documentation that many developers want from a library.

2021-05-12T14:53:15.082100Z

You can either accept that as a decision of the Clojure maintainers that seems unlikely to ever change, or you can go to alternate resources like http://ClojureDocs.org that say more, but are not "official" but community-contributed, or a book like https://www.manning.com/books/clojure-the-essential-reference that is very deep in details of the behavior of the implementation, but again written by someone who is not a Clojure maintainer, but has spent large chunks of time analyzing the Clojure implementation.

Ivan Koz 2021-05-12T14:53:28.082500Z

right, most of the times reading clojure sources is enough for me, yet sometimes the thing is not obvious

2021-05-12T21:11:10.086200Z

I have this code:

(def rules {"mov" [has-two-arguments? first-argument-is-a-register?]
            ,,, other keys and rules elided.})

(defn validate-instruction [instr args]
  (keep (fn [f]
          (let [error (f args)]
            (when error
              (str "Invalid `" instr "` call, " error)))) (rules instr)))
All my rules take one argument, so this is working fine. However, I'd like to extend it so that I can have rules with 1 argument and rules with 2 arguments... The obvious solution (that I don't like), is just to make all the rules 2 arguments and discard one on most of them... The other option is to remove all this and just hard code a function for each key in rules. BUt I don't like that either. Can anyone think of a better way?

phronmophobic 2021-05-12T21:20:50.086400Z

Have you considered using spec? This type of validation seems like something spec would both support and be a good fit for.

2021-05-12T21:21:44.086600Z

I know nothing about spec, I thought it was a thing used for validating inputs to functions etc. Can it be used as a general purpose thing for doing validation?

2021-05-12T21:21:59.086800Z

I mean, I can just valid any old string ?

phronmophobic 2021-05-12T21:22:11.087Z

yes and yes. Not only that, it can generate data matching your spec

phronmophobic 2021-05-12T21:22:34.087200Z

is the instruction a string?

phronmophobic 2021-05-12T21:23:06.087400Z

Generally, I would usually separate parsing into its own step where you parse a string into data.

phronmophobic 2021-05-12T21:23:19.087600Z

and then the instructions would just be data

2021-05-12T21:25:04.087900Z

yes, so I have a little toy language I've written. With very basic assembly language style instructions. e.g. "mov :a 5" I want to parse this as things like has 2 instructions, first instruction is a register (i.e. it starts with a : ) But I could also have "mov %1 %2" if this mov was declared in a macro (I support macro expansion). So when validating "mov %1 %2", the rules are sorta different, since its in a macro I want to check fi the first argument starts with % as I know this will be expanded out to a register at macro-expansion stage. SO the function needs 2 inputs, the args and a bool for whether its in a macro or not.

2021-05-12T21:26:13.088100Z

hmmm, maybe the solution is just to write a proper EBNF or something

2021-05-12T21:27:07.088300Z

I'll look into spec though, I didn't realise you could use it like that

phronmophobic 2021-05-12T21:27:26.088500Z

yea, although if you're ok with a simple grammar at first. parsing can be simple as line-seq and (clojure.string/split line #" ")

phronmophobic 2021-05-12T21:30:58.088700Z

learning spec and something like instaparse may be a little too much yak shaving depending on your goals. Another option that might be interesting is just 1. parse using line-seq and clojure.string/split 2. create multimethods for validating and processing instructions that look like ["mov" arg1 arg2] that dispatch on first

phronmophobic 2021-05-12T21:32:25.088900Z

You can also create a simple parser with java.util.Scanner .

2021-05-12T21:36:05.089100Z

Think I might try with spec, as the whole point of this project is just to help me learn clojure and I wanted to look into spec at some point

👍 1
1
2021-05-12T21:36:55.089400Z

thanks for your advice

phronmophobic 2021-05-12T21:39:02.089600Z

I haven't seen spec used for just validating strings. spec will be much more effective if it's validating a string that's already been parsed into data, but there might be a good way to use it on validating strings directly

2021-05-12T21:42:05.089900Z

I can have it parsed into [:mov :a 5] or [:add :a :b], [:call "foo"] etc at this point. That part already works great. WOuld spec be better used on these structures?

2021-05-12T21:42:27.090100Z

and i validate on these?

phronmophobic 2021-05-12T21:42:42.090300Z

:thumbsup:

Mike Martin 2021-05-12T22:35:00.091700Z

Currently when I find a clojure library I want to try out that doesn’t specify it’s full maven name and version I go to https://mvnrepository.com/ and look up the artifact to find that information so I can add it as a dependency in deps.edn. Is there a more idiomatic way to do this?

2021-05-12T22:56:51.092Z

I'm using a piece of java-based proprietary software that allows you to extend certain of its classes. This basicially wraps a single function in a bunch of Java ceremony that allows it to fit into the rest of the system.

(ns my.ns.Add1
 (:require ...)
 (:import ...)
 (:gen-class
 :extends their.AbstractClass
 :yadda
 :yadda
 :yadda
)

(defn -doit [x] (pack (inc (unpack x))))
So Add1 works, but I'm hoping I can write a macro that works something like this:
(defmacro def-doer [func arg-description])

(def-doer Add1 inc [long long]) =&gt; class my.ns.Add1
(def-doer IsPrime prime? [boolean long]) =&gt; class my.ns.IsPrime
;; etc
So here's where my mental model breaks down. When I spell out the gen-class explictly, lein compile has a named class it can generate, but invoking the equivalent macro seems to introduce some added level of indirection, and no class gets defined. Could I trouble someone to clue me in here? Thanks,

2021-05-12T23:00:49.092400Z

Any idea why I’m seeing this error? Everything else executes well

NoahTheDuke 2021-05-12T23:01:43.093700Z

Clojars? Most libraries link directly to their clojars page. You can also use the git repo in deps.edn

dpsutton 2021-05-12T23:02:11.093900Z

I can recreate this error with mal-formed edn:

search=&gt; (clojure.edn/read-string "{:a}")
Execution error at metabase.api.search/eval125364 (REPL:2235).
Map literal must contain an even number of forms

2021-05-12T23:02:32.094100Z

So the problem is with the EDN?

dpsutton 2021-05-12T23:03:38.094600Z

if you read the stacktrace by evalling *e you would know for sure. But seems like a good guess. You're reading edn data structures and getting an error that there's a map literal with an odd number of forms. the form you are evaluating has only a single well-formed map in it

2021-05-12T23:06:03.095Z

2021-05-12T23:06:13.095200Z

How would I read this to know for sure?

dpsutton 2021-05-12T23:06:35.095400Z

the top of that stacktrace is the function which through the error

dpsutton 2021-05-12T23:07:29.095600Z

Util isn't super helpful, but the next line is EdnReader MapReader, which is pretty descriptive. The function that reads edn has a function that reads maps and it threw an error that maps must contain an even number of forms

2021-05-12T23:08:12.095800Z

Is there a way I could find where the map has an uneven number of forms?

dpsutton 2021-05-12T23:08:17.096Z

and a few lines below that is read_eval_print, which is run in a loop and is your REPL

dpsutton 2021-05-12T23:08:23.096300Z

it's not in your app

dpsutton 2021-05-12T23:08:54.096600Z

its in the file "/Users'roberthaisfield/Dropbox/Roam-ups/..."

2021-05-12T23:09:42.096800Z

So how would I identify the error point in that file?

2021-05-12T23:10:07.097Z

there aren't distinct syntax classes for keys and values, so the reader can't tell if one is missing, it can only tell that it got to the end of the map and read an uneven number of things

2021-05-12T23:12:44.097200Z

if you know, for example, that all the keys should be keywords and none of the values are, you can create an input stream from the file and read one character (skipping past the initial {) and then read forms from the input stream checking that the first one, and then everyone after that is a keyword

2021-05-12T23:13:31.097400Z

that of course assumes the outermost form is a map and it is that map that has the issue

2021-05-12T23:14:52.097600Z

my guess is you have a '...' somewhere in the map, because you printed at the repl with *print-level* and/or *print-length* bound

2021-05-12T23:17:35.097800Z

user=&gt; (binding [clojure.core/*print-length* 3] (prn {:a 1 :b 2 :c 3 :d 4}))
{:a 1, :b 2, :c 3, ...}
nil
user=&gt;
something like that

2021-05-12T23:18:08.098Z

where likely you didn't even know that *print-length* was set, because your tooling sets it for you

2021-05-12T23:18:24.098200Z

and you just spit that printed invalid/truncated map out to a file

dpsutton 2021-05-12T23:18:42.098400Z

yeah. you can visually inspect the file. you're in something that has structural movements i'd start trying to identify sub parts that were valid edn to narrow down the tree

2021-05-12T23:20:43.098600Z

Okay, I think I might know what the problem was. Opening up the EDN directly in a text editor a while ago reformatted it in some way (VS Code asked to fix something) This time I just redownloaded an EDN export and didn’t open it directly (except through code) and that worked

1
Mike Martin 2021-05-12T23:20:47.098800Z

totally forgot that deps.edn has git support, that probably would suffice for my cases. Thank you!

👍 1
2021-05-12T23:21:05.099200Z

Thank you for helping. This is helping me understand how to find the issue better

dpsutton 2021-05-12T23:22:15.100200Z

weird. glad its sorted