Fork me on GitHub
#beginners
<
2019-01-23
>
Charlot01:01:17

Hello. Working on a piece of genetic programming, and I have the situation where I want to have a set of symbols that will be placed in a list. Later when I am evalling that list as a function, I would like to be able to set the values of those symbols, with something like a let

Charlot01:01:44

example my program evolves a function (* (- 4 x) y). I would like to be able to provide values for x and y when I am running my fitness function

hiredman01:01:14

that is a function

hiredman01:01:30

(an expression parameterized over some values)

Charlot01:01:51

Except I don't have values for x and y at first, and need to provide/define them later

hiredman01:01:59

but that is a function

hiredman01:01:26

(fn [x] x) is "I don't have a value for x and will provide it later"

Charlot01:01:03

Ah, I think I see.

Charlot01:01:56

Okay, no, that is not it. I am trying to evolve programs, so I have a function that builds nested lists, ensuring that the first argument is a function, and any subsequent list items are either valid s-expression or terminals like 1

Charlot01:01:50

thus the whole thing can be evaled and produce a result

Charlot01:01:12

I'm seeking a way to do two things. One, include terminals like x or y in the set of possible chosen values. Two, be able to call an evolved function, with the values of x and y present.

Charlot01:01:29

I would like to avoid using state, and having every 'x' terminal be "look up the value in an atom" but that could work

Charlot01:01:26

it seems refs or vars are close, but then I am unsure of how to use them precisely. I'm going to go read up on them

hiredman01:01:13

those are the same thing

hiredman01:01:04

not refs and vars, but the two things you want to do

Charlot01:01:08

... guess that's why i need to read up on them. šŸ˜“

lilactown01:01:38

since you are building up a clojure form to be evaled, what stops you from wrapping the whole list in a (let [x ... y ...] ...)

lilactown01:01:45

or a defn and then calling the function

hiredman01:01:46

you want to be able run some code multiple times with different values for x and y

hiredman01:01:07

in some cases changing the values, in other cases not

hiredman01:01:14

make your code a function and pass in x and y

Charlot01:01:54

I'll try and hammer on it for a while, see if the lightbulb pops.

Charlot01:01:09

Thank you both though, I hope I do not sound ungrateful.

Alex Miller (Clojure team)01:01:12

you could use with-local-vars too (which is almost never used in Clojure)

mpcarolin01:01:22

What is the reason for not being able to use java static functions like clojure functions? As an example, I want to do this: (every? Character/isUpperCase [\a \b \c]), but am told that Clojure is ā€œUnable to find static field isUpperCase in class java.lang.Characterā€. consequently I have to wrap it like this: (every? #(Character/isUpperCase %) [\a \b \c])

andy.fingerhut01:01:18

An unsatisfying but true answer is "Because Rich Hickey designed it that way", leading immediately to the question "Why did Rich Hickey design it that way?" I do not know the answer to that question, but suspect it may have something to do with the JVM mechanisms chosen for function calling, versus straight JVM method calls, and efficiency.

andy.fingerhut01:01:55

Or perhaps also the flexibility rather than efficiency of Clojure function calls, i.e. having variable number of args.

mpcarolin01:01:10

A little unsatisfying šŸ˜„ but thanks

mpcarolin02:01:32

Ah thatā€™s interesting and explains a lot. Thank you!

Alex Miller (Clojure team)02:01:03

with MethodHandles now, it might be more feasible to build this into the compiler directly now

Jin Choi02:01:15

I have an issue with a quiz from 4clojure http://www.4clojure.com/problem/55 My solution to the problem works on my host. user=> (defn solved? [f] (= (f [1 1 2 3 2 1 1]) {1 4, 2 2, 3 1})) #'user/solved? user=> (solved? #(->> % (group-by identity) (into {} (map (fn [[k v]] [k (count v)]))))) true However, the function #(->> % (group-by identity) (into {} (map (fn [[k v]] [k (count v)])))) doesn't pass on 4clojure website with the following error message: clojure.lang.ArityException: Wrong number of args (1) passed to: core$map Is it a version issue? Any idea?

seancorfield02:01:53

Very likely http://4clojure.com is running an older version of Clojure that doesn't have transducers.

seancorfield02:01:45

Based on https://github.com/4clojure/4clojure/blob/develop/project.clj it seems to be running Clojure 1.4 perhaps? I would have expected someone to have updated it a bit in the last six years... No sure who maintains it these days.

Jin Choi02:01:04

Thanks @seancorfield Could you suggest me another implementation for the problem without using transducer?

seancorfield02:01:55

Sure, just transpose the into/`map` calls to avoid that arity.

seancorfield02:01:32

(solved? #(->> xs (group-by identity) (map (fn [[k v]] [k (count v)])) (into {})))

seancorfield02:01:49

(not sure if I typed that right, but that's the general idea)

Jin Choi02:01:05

user=> (solved? #(->> % (group-by identity) (map (fn [[k v]] [k (count v)])) (into {}))) true

Jin Choi02:01:29

I will dig more into what the transducers are. Thank you very much šŸ™‚

seancorfield03:01:09

Your (into {} (map (fn ...))) form gets the expression threaded into the last argument position of the into call, but your map call only has one arg -- the function -- so that is valid with transducers (but was invalid before).

seancorfield03:01:45

So you get (into {} xform expression) where xform is the transducer returned by the 1-arg map call.

Jin Choi03:01:47

Perfectly explained. I didn't even mean to use transducers when I came up with my previous implementation. Now I understand that I accidentally used the form of (into {} xform expression). Although I met your version of implementation without transducers to workaround the old version of 4clojure, I think yours look more elegant and simpler. Thanks for your lesson.

Jin Choi03:01:28

BTW, can I run a clojure with a specific version with lein repl, say, clojure 1.4.0 on my host for a testing and debugging purpose?

Daouda03:01:38

hey guys, i want to play around with clojure spec, so i've generated a project with lein, my problem is that i can't get the repl to invoke functions i've created šŸ˜ž

Daouda03:01:02

(ns demo-spec.core
  (:gen-class)
  (:require [clojure.spec :as s]
            [clojure.spec.test :as test]))

(defn my-index-of
  "Return the at which search appear in source."
  [source search]
  (str/index-of source search))

(my-index-of "foobar" "b")
(apply my-index-of ["foobar" "b"])

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (println "Hello, World!"))

Daouda03:01:37

(defproject demo-spec "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url ""
  :license {:name "Eclipse Public License"
            :url ""}
  :dependencies [[org.clojure/clojure "1.10.0"]]
  :main ^:skip-aot demo-spec.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})

Daouda03:01:32

in project root i ran lein repl but can't call any of the functions šŸ˜ž

andy.fingerhut03:01:37

You did not see any error messages when you ran lein repl? When I try to reproduce what you have done, it says there is no such namespace clojure.spec, which should be instead clojure.spec.alpha, and similarly clojure.spec.test.alpha. Also there should be a require of [clojure.string :as str] or else the str/index-of symbol is not resolved.

Daouda03:01:16

[clojure.main main main.java 37]]}
nREPL server started on port 46065 on host 127.0.0.1 - 
REPL-y 0.3.7, nREPL 0.2.12
Clojure 1.10.0
Java HotSpot(TM) 64-Bit Server VM 1.8.0_191-b12
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

andy.fingerhut03:01:34

What is above the first line you pasted?

andy.fingerhut03:01:14

That looks like the last line of a stack trace, which I saw, where the first few lines had an error message

andy.fingerhut03:01:03

If you see lines like that, it can be a sign of a problem.

Daouda03:01:02

thank you šŸ™‚

Daouda03:01:24

i'll make some changes and see

Charlot04:01:59

For reference: I ended up with a solution to my issue like so

(defn expression-to-function [arguments expression]
  (eval (list 'fn arguments expression)))
with arguments being a list of quoted symbols.

andy.fingerhut04:01:14

Also this isn't necessarily a problem, but you should be able to do experimentation without using (:gen-class), aot, or mentions of uberjar in your project.clj file. You might need such things if you want to deploy a compiled Clojure application, but for REPL experimentation they are not necessary, and can in some scenarios cause issues.

Daouda04:01:50

ok i got it

Daouda04:01:44

@andy.fingerhut i did those changes you mentioned and it's working fine now, thank you šŸ™‚

dangercoder06:01:31

Is there any handy way to transform a map with namespaced keys, like this:

#:funky-cool-aid {:color :purple}
to just
{:color :purple} 
? I know one can use destructuring and create a new map but my version of that is quite verbose.

seancorfield06:01:16

(reduce-kv (fn [m k v] (assoc m (keyword (name k)) v)) {} your-map) should do it

seancorfield06:01:58

Or (into {} (map (juxt (comp keyword name) identity) your-map)) if you're feeling sassy šŸ™‚

dangercoder06:01:50

@seancorfield thank you, I'll have a look at both, it will be used quite frequently so i'll add them to danger-util āœŒļø

seancorfield06:01:06

(I should resist trying to write a point-free version at ten at night)

dangercoder06:01:27

7:04 in sweden šŸ˜…

seancorfield06:01:48

My second attempt was wrong -- I'd need a bit more in there

seancorfield06:01:38

(into {} (map (juxt (comp keyword name key) val) your-map)) maybe something like that... I should open a REPL back up...

dangercoder06:01:19

i just tried it, it works with the updated version šŸ™‚

seancorfield06:01:08

user=> (def your-map  #:funky-cool-aid {:color :purple :taste :grape})
#'user/your-map
user=> your-map
#:funky-cool-aid{:color :purple, :taste :grape}
user=> (into {} (map (juxt (comp keyword name key) val) your-map))
{:color :purple, :taste :grape}
user=>   
yup

seancorfield06:01:50

Although I think it's more that Grape Kool-Aid tastes "purple flavor" šŸ™‚

seancorfield06:01:20

I don't advise the point-free style really. Just wanted to show it was possible.

dangercoder06:01:28

šŸ‘ thank you for the time @seancorfield

kaneko08:01:47

I have a map where each value is an array of ints. For example: {:a [1 2 3], :b [4 5 6]} I want to update this map so that the arrays no longer contain even numbers: {:a [1 3], :b [5]}. What is the idiomatic way of doing this?

danielneal10:01:01

@r.natarajan35 one way of processing maps keys and values is to use reduce-kv e.g

(reduce-kv (fn [m k v] (assoc m k (filterv even? v))) {} {:a [1 2 3], :b [4 5 6]})

danielneal10:01:46

oh wait, no longer contain even numbers

danielneal10:01:50

s/even?/odd? in the above

danielneal10:01:28

another common approach is to use map-vals, although it's not in core, it's commonly implemented

kaneko10:01:33

@danieleneal Oh wow, thanks! I really like the list comprehension version, it makes what i'm doing very clear to read

Shriyans11:01:19

what is the recommended linter and formatter tool ?

pavlosmelissinos11:01:33

there was a thread a while back

pavlosmelissinos11:01:18

Probably eastwood, I'd say

Shriyans11:01:35

thanks for the link. šŸ™‚ afaik eastwood is linter any formatter reco too ?

pavlosmelissinos11:01:03

nope, sorry šŸ˜ž

Shriyans11:01:07

no worries thanks

schmee11:01:49

@r.natarajan35 just as an alternative, hereā€™s a Specter solution:

user=> (def a {:a [1 2 3], :b [4 5 6]})
#'user/a

user=>
user=> (setval [MAP-VALS ALL even?] NONE a)
{:a [1 3], :b [5]}

Sy Borg11:01:40

Question about destructuring of nested maps - why the outermost key is specified last in such expressions? E.g., let's say I have

(def data {:platform "Windows" :type {:name "2008 R2" :edition "Standard"}})
This works:
(let [{{:keys [name edition] } :type} data]
(println name edition ))
2008 R2 Standard
but not this:
(let [{:type {:keys [name edition] }} data]
(println name edition ))
Syntax error macroexpanding clojure.core/let at (form-init5616907436889296336.clj:1:1).
[...]

noisesmith18:01:06

one pragmatic reason is that this means you can use the same key for multiple bindings:

ser=> (let [{[{:keys [a b c]}] :maps [_ map2] :maps} {:maps [{:a 0 :b 1 :c 2} {:a 33}]}] {:a a :b b :c c :map2 map2})
{:a 0, :b 1, :c 2, :map2 {:a 33}}

noisesmith18:01:45

if the normal key / value order was preserved, the map wouldn't be legal - you can't use the same key twice

Sy Borg12:01:38

I've come just from there

borkdude12:01:52

(let [[{{:keys [name edition] } :type} data] ..): it takes the value of :type in data and then you do an additional destructuring on that

Sy Borg12:01:36

I get it, I was confused about the order

danielneal12:01:46

Yeah, map destructuring is done in pairs, and in each pair the thing you're destructuring is on the right hand side and the symbols/names that you want to bind it to are on the left hand side. This is similar to how in a let binding, the thing is on the right hand side, and the name on the left, you'd say (let [a 3] a)

Sy Borg13:01:50

@danieleneal yes, I would think so if it was about data symbol position, but it's about a key

Machado15:01:24

Hey, there!

Machado15:01:39

(ns my-app.database)

(def database (atom {}))

(defn reset-database [] (reset! database {}))

(defn add-record [table record]
  (swap! database update-in [table] conj (into {} record))
  {:ok record})

(defn remove-record [table record]
  (swap! database update-in [table]
         (fn [records]
           (remove #(= record %) records))))

(defn get-all-records [table] (get @database table))

(defn get-record-by-id [table id]
  (->>  (get-all-records table)
        (filter #(= id (:id %)))
        first))

(defn update-record [table id update]
  (let [record (get-record-by-id table id)
        updated-record (merge record update)]
    (swap! database update-in [table] record updated-record)))

Machado15:01:51

I just wrote this atom to have a simple in-memory database

Machado15:01:10

but if I use a record in the update-record

Machado15:01:22

it says that it can't use a record as a Lang.FN

Machado15:01:38

what I'm doing wrong?

tavistock15:01:28

swap turns into basically (update-in @database [table] record updated-record)

tavistock15:01:00

update-in expects a fn as 3rd arg

tavistock15:01:40

you might mean to put record in that vector with table

Machado15:01:36

I have a vector with maps

Machado15:01:41

and I want to update a value inside this map

Machado15:01:49

but apparently doing this is not usual in clojure

tavistock15:01:50

and you probably want assoc-in

Machado15:01:24

I have ({:id 1 :name "Gabriel", {:id 2 :name "John"})

Machado15:01:32

and want to change name to "Jane"

Machado15:01:35

where id is 2

Machado15:01:42

but I could not figure it out

Machado15:01:53

without using a map

tavistock15:01:45

change your db to use the id as an index and it would be alot easier but yea i think you would need to use a map

Machado15:01:11

you mean using {id {record}}

enforser15:01:25

yes. provided id is a unique value

tavistock15:01:28

try using {1 {:name "Gabriel"} 2 {:name "John"}}

tavistock15:01:26

but the other way works too, youā€™d just need to use a (map ...) that checks if the id is correct then run the update when it is correct.

enforser15:01:14

I would advise against using map in that case. Map will iterate over every single entry in the DB.

enforser15:01:39

(defn update-entry
  [pred f coll]
  (let [[coll1 [entry & coll2]] (split-with (complement pred) coll)]
    (lazy-cat coll1 [(f entry)] coll2)))

(update-entry odd? inc [2 2 2 1 2 2])
;; => (2 2 2 2 2 2)
This seems like a decent way to update a single entry of a sequence, but obviously just using a map is much better.

jaide16:01:01

If Iā€™m writing a small utility to replace all :smile: shortcodes in a markdown document with the emoji characters, how should I be parsing it? Is there a parser combinator library someone could recommend or should I use a line-seq and regex?

manutter5116:01:55

Check out Instaparse (and the #instaparse channel)

aengelberg17:01:16

Instaparse is good for turning text into data, e.g. parsing a programming language or a custom data structure DSL. For "search/replace" type scenarios, where you have mostly unimportant text and an occasional token you care about, I think a regex is better honestly.

jaide19:01:43

@U0516PHE3 Thanks for the explanation. After I read the readme of it I realized it was a bit overkill and went with a function to replace the shortcodes line-by-line. It seems to work pretty well so far.

macgyver16:01:41

Hello, I am having issues running a Leiningen project in the repl within vscode via the Clojure extension. Where should I start to troubleshoot this? version: Leiningen 2.8.3 on Java 1.8.0_191 OpenJDK 64-Bit Server VM error:

Warning: implicit middleware found: cider-nrepl.plugin/middlewarePlease declare all middleware in :middleware as implicit loading is deprecated.

zane17:01:11

@U7J6AMWDR Is the project public, or could you share it with us?

zane17:01:20

The cider part of cider-nrepl.plugin/middleware refers to CIDER, a Clojure development environment for Emacs. https://github.com/clojure-emacs/cider

zane17:01:41

Seeing as you say you're using VSCode I wonder if there's something strange about how the project is being started.

macgyver19:01:31

@U050CT4HR it is just a newly generated project using Leiningen. I just didn't want to have to learn vim at the same time I am learning vscode. Any thoughts?

zane20:01:58

Perhaps something in your ~/.lein/profiles.clj refers to CIDER?

macgyver22:01:17

I don't have a ~/.lein/profiles.clj folder. Any other thoughts? I set up Leiningen in the way described by the install docs - don't think there was anything unique about it

macgyver23:01:31

looks like it is related to an existing bug here: https://github.com/avli/clojureVSCode/issues/121

zane03:01:05

I see! Glad you were able to get that figured out.

jstaab18:01:45

Is there a good analog to mocha's only method (https://mochajs.org/) in cljs.test (or clojure.test for that matter)? I have ~100 tests and I'm getting tired of commenting out un-used tests and/or changing the regular expression on my run-all-tests call.

jstaab18:01:49

While I'm at it, is there a good alternative to cljs.test? I'm pretty unhappy with it in a number of aspects, and I'm on the cusp of writing my own testing framework šŸ˜•

šŸ˜¢ 5
dpsutton18:01:20

You could check out kaocha

jstaab18:01:36

oh yess thank you

jstaab18:01:40

that looks so much better

jstaab18:01:05

Looks like clojurescript support is pretty new, any idea if it works with shadow-cljs?

jstaab18:01:18

Or at least, code written for shadow-cljs

borkdude19:01:11

@jstaab what are you unhappy about?

borkdude19:01:01

if you want to run a single test, you could give https://github.com/Olical/cljs-test-runner a try

zlrth19:01:10

say i really wanted to check whether a value was false like so:

(s/explain #{false true} false)
val: false fails predicate: :clojure.spec.alpha/unknown
:clojure.spec.alpha/spec  #{true false}
:clojure.spec.alpha/value  false
that is, i didn't want to use boolean?. how would i do that?

noisesmith19:01:11

the fact that sets work as predicates relies on the set returning the arg if present, this doesn't work for nil or false

zlrth19:01:39

ah contains?

noisesmith19:01:42

you can use #(contains? #{false true} %} but that doesn't give you a free generator like a set does

borkdude19:01:50

#(contains? #{true false} %)yeah

zlrth19:01:12

@borkdude the spec should fail if it's any value other than false and true

zlrth19:01:16

yeah thanks

noisesmith19:01:24

you can use boolean? here of course

borkdude19:01:30

(s/with-gen ... (fn [] boolean?))

didibus19:01:39

Why not boolean? Just a contrived example for learning?

noisesmith19:01:24

maybe boolean? already has a generator defined? my spec knowledge is a little rusty

zlrth19:01:30

i'd like to use this as output for less-technical people, so i want really explicit error messages

noisesmith19:01:49

I think "boolean?" is pretty self explanatory

zlrth19:01:01

i tried expound, but it didn't explicate the acceptable values for boolean?

zlrth19:01:08

(not that it should have to)

zlrth19:01:47

i think it is self-explanatory, but i'm trying to be very careful

jstaab19:01:47

@borkdude Some of the trouble is incidental and because of shadow-cljs, but off the top of my head: - No way to target individual tests without writing code. You can target namespaces with a regular expression or run-tests, but I'd really love command-line or local-code selection mechanisms (like kaocha offers) - I've had trouble getting tests to exit with a 1 for ci purposes. I can't remember how I got that working, but I may have wrapped them with a bash script that parsed the output - There aren't a lot of nice assertion methods. Not a big deal; it's easy to write your own, but is= is the big omission here. - Unless this was an artifact of shadow-cljs's test runner, stack traces get swallowed. If there's an error, I want to see the whole thing. As a result, I'm passing in a custom formatter that calls *print-err-fn*.

didibus19:01:59

@mfm Hum, there's a library for that, let me try an find it. Because shoe-horning simpler spec seems wrong.

zlrth19:01:12

interesting! thanks

borkdude19:01:17

@mfm

ClojureScript 1.10.439
cljs.user=> (require '[clojure.spec.alpha :as s])
nil
cljs.user=> (s/explain boolean? true)
Success!
nil
cljs.user=> (s/explain boolean? false)
Success!
nil
cljs.user=> (require '[clojure.spec.gen.alpha :as gen])
nil
cljs.user=> (require '[clojure.test.check])
nil
cljs.user=> (gen/sample (s/gen boolean?))
(false true true false true true true true false false)

borkdude19:01:28

@jstaab cljs-test-runner should get you most of these things as well, I havenā€™t tried kaocha, itā€™s cljs support is pretty new

jstaab19:01:31

@borkdude Yeah, just gave it a shot; it doesn't detect my dependencies as declared in my shadow-cljs.edn, so I'm looking for a way around that. I'll take a look at cljs-test-runner though; that might be closer to a happy medium.

borkdude19:01:28

youā€™d have to put those deps in a deps.edn file or maybe you can point the cljs-test-runner to a different file, I havenā€™t tried and donā€™t know if shadow uses the same format as tools.deps

jstaab19:01:21

It's more leiningen-style, but I only use mvn deps so far, so I could maybe translate. Anyway, it's probably easier to just duplicate my dependency list. The one thing I'm a little worried about is whether either one will know how to import node_modules dependencies, since I think shadow-cljs gets pretty deep into resolution/externs stuff

borkdude19:01:59

ok, donā€™t know about that

jstaab19:01:13

heh me neither šŸ™‚

zlrth19:01:29

so you're saying, i could gen/sample some valid results to show the user? that's pretty cool

zlrth19:01:36

didn't think of that

borkdude19:01:51

@mfm Iā€™m saying the spec you want is boolean?

zlrth19:01:30

i agree that the spec that behaves correctly is boolean?; i was looking for a spec whose explanation says, "value foobar is not one of #{false true}"

borkdude19:01:59

why is that any better than not boolean?

zlrth19:01:16

for my intended audience, boolean is not obvious

šŸ‘ 5
zlrth19:01:25

thanks @didibus! will investigate

zlrth19:01:03

phrase looks great!

didibus19:01:44

@borkdude If the audience is an end-user, boolean is bad feedback. You want the message to be simpler, and more contextualized to the app and the domain.

borkdude19:01:48

where exactly do you use this @mfm?

zlrth19:01:20

the intended audience is sort of an admin dashboard for our "tech support engineers" who have to diagnose mis-parsed client data. so it'd be great to have "value foobar is not one of #{some acceptable values}"

zlrth19:01:06

that is, my expectation is that they can look past some syntactic idiosyncracies ( #{} and the like ) but i'd still like it to be really really concrete

zlrth19:01:24

agreed that it's a relatively rare use case

borkdude19:01:29

you could also hook your own explain printer thing maybe

zlrth19:01:55

yeah i've been reading into that a little bit. and, in fact, using gen/sample is a great idea

noisesmith19:01:24

you could also implement an explaining layer that maps a spec to a freeform string customized to your audience

noisesmith19:01:33

{boolean? "should be either 'true' or 'false'" number? "should be be a machine readable number like 1 or 3.14"} etc.

noisesmith19:01:27

you can get pretty far on specs almost doing the right thing with explain, but I'd assume you'd keep getting more awkward messages and corner cases

lmergen19:01:11

typically the expound + orchestra combo is good enough for me

noisesmith19:01:35

I think that would depend on who is intended to interpret the message, if the reader isn't a dev (not to mention not a clojure dev), I'd expect a lot of awkward details

noisesmith19:01:35

eg. even something like "string" can be confusing for a technical user who isn't a dev

lmergen19:01:04

i guess writing your own spec introspection util is one way to get this "right", so that you can optimize for your specific audience

borkdude19:01:09

in that case you could also write your own predicates and give them less technical names

šŸ‘ 5
lmergen19:01:15

the phrase library is another solution

zlrth19:01:57

great comments, thanks

bbrinck19:01:27

@mfm Sorry if I missed this, but if you are in a position where you can define your own spec for boolean, you can use expound to set a custom error message

bbrinck19:01:31

(require '[expound.alpha :as expound])
(require '[clojure.spec.alpha :as s])

(set! s/*explain-out* (expound/custom-printer {:print-specs? false}))

(s/def ::boolean boolean?)

(expound/defmsg ::boolean "should be either true or false")

(s/explain ::boolean false)
;; Success!
(s/explain ::boolean nil)
;; -- Spec failed --------------------

;;   nil

;; should be either true or false

;; -------------------------
;; Detected 1 error

bbrinck19:01:34

You canā€™t associate a message with the boolean? predicate itself, but you can associate it with spec keywords that are predicates

borkdude19:01:33

@bbrinck Ah, now I get why you wanted those keyworded specs in speculative šŸ™‚

šŸ˜‰ 5
bbrinck19:01:55

indeed šŸ™‚

borkdude19:01:13

very cool. have you used them anywhere yet?

bbrinck19:01:49

I havenā€™t yet used them in speculative, but when I get some more free time, I have an idea about building a set of beginner-friendly messages for lots of core predicates

bbrinck19:01:03

expound + custom messages + speculative + rebel-readline

borkdude19:01:31

awesome. I think I want a custom KLIPSE repl (faster performance) online with exactly that too

bbrinck19:01:52

that would be sweet

bbrinck19:01:30

Outside of the beginner context, I know that lucinia uses this functionality to clarify some complicated specs https://github.com/walmartlabs/lacinia/blob/d351d3c4dd9a0aad5cd078c87fabed0df2b14471/src/com/walmartlabs/lacinia/expound.clj#L22-L28

borkdude19:01:11

@bbrinck Iā€™ve been wondering if expound can handle (s/or :f ::foo :b ::bar) well, no need to put composites in their own keyword right?

bbrinck19:01:33

I suppose it depends on what you mean by ā€œhandleā€ šŸ™‚

bbrinck19:01:42

This is what happens by default:

(s/def ::foo int?)
(s/def ::bar string?)
(s/def ::foobar (s/or :f ::foo  :stuck_out_tongue: ::bar))

(s/explain ::foobar :abc)
;; -- Spec failed --------------------

;; :abc

;; should satisfy

;; int?

;; or

;; string?

;; -------------------------

bbrinck19:01:15

@borkdude do you mean handle custom messages for :foo or :bar?

noisesmith19:01:31

:D :stuck_out_tongue:

bbrinck19:01:07

I was going to edit, but thatā€™s too good

borkdude19:01:19

@bbrinck yeah, so if you would have custom messages for either spec they would still be printed here?

bbrinck20:01:26

Individual messages will work

bbrinck20:01:29

(expound/defmsg ::foo "should be an integer")
(expound/defmsg ::bar "should be a string")
(s/explain ::foobar :abc)
;; -- Spec failed --------------------

;;   :abc

;; should be an integer

;; or

;; should be a string

;; -------------------------

bbrinck20:01:07

but if you want a message for the whole thing you have to wrap in a predicate

borkdude20:01:28

it would also be helpful to have a 4clojure instance running with expound + speculative I bet

šŸ‘ 5
borkdude20:01:54

@bbrinck the or printing is good, thanks!

alex31415921:01:10

Hello - I've tried uploading a simple project to Clojars (cljblpapiwrapper). That project uses a local jar file (non Clojure) as a dependency, declared in :resource-paths in project.clj. Now upon trying to use this project somewhere else, it fails to locate the jar file. How should I go around the problem? Can I publish the jar on Clojars separately? Thank you,

noisesmith22:01:07

the natural way to share deps is to make maven artifacts, and it's straightforward to use clojars for that, sure

noisesmith22:01:40

or they could check out the repo and run lein install

jaide22:01:03

If a clojure file defines a private function like (defn- a-func [] true) is there a way to call it like (in-ns 'my-private.ns) (my-private.ns/a-func)?

hiredman22:01:06

it is a bad idea to put in-ns forms in the middle of a source file unless you really know what you are doing

jaide22:01:22

Sorry I mean call it from within a repl

hiredman22:01:27

@#'a-func will let you call the private function

Alex Miller (Clojure team)00:01:16

You donā€™t need to deref - vars are invokable

hiredman22:01:12

(which manually does the bits the compiler would do for you if it wasn't so worried about things being private)

jaide22:01:41

Hmm shouldnā€™t that work? https://github.com/weavejester/environ/blob/master/environ/src/environ/core.cljc#L80 Iā€™m trying to run the following in a REPL

(require 'environ.core)
(in-ns 'environ.core)
(def env (#'read-env))
When I run it I get ā€œunable to resolve var read-env in this contextā€.

hiredman22:01:32

don't do the in-ns

hiredman22:01:02

make sure the version of the code you are reading matches the version of the mode your are running

jaide22:01:17

Oh, good point

jaide22:01:37

šŸ˜µ That was the issue. It was refactored since the latest release. Thanks for helping me catch that.