Fork me on GitHub
#clojure
<
2018-07-04
>
samueldev03:07:58

Unless I'm misunderstanding something, clojure spec (s/keys :req-un [::foo ::bar]) is a way to spec a map, but it operates under the assumption that the key name in the map matches the name of the spec definition

samueldev03:07:09

How would I go about spec'ing a map without that assumption?

samueldev03:07:42

I.E. I'd like the keys in my map to be :type and :text for example, but I'd like my spec to be defined as (s/def ::button-type ...) and (s/def ::button-text ...)

seancorfield03:07:14

(s/keys :req-un [:button/type :button/text]) is close to what you want.

seancorfield03:07:30

:req-un means unqualified keys.

seancorfield03:07:15

So your map would have :type and :text and the key-specs for them could be :button/type and :button/text -- does that help @samueldev?

samueldev03:07:46

Kind of, what are my (s/def)s in this scenario? (s/def :button/type)? (omitted double ::?)

seancorfield03:07:44

::type is just shorthand for :this.namespace/type

seancorfield03:07:56

So it's just a regular qualified keyword.

seancorfield03:07:07

(for whatever this namespace is)

seancorfield03:07:32

So you can have two maps with plain :text keys but different specs.

samueldev03:07:52

okay that did it! thanks @seancorfield. just for my understanding, I can still access this spec from another namespace, even if its unqualified?

samueldev03:07:01

here's the full code fyi that works now šŸ™‚

(s/def :button/type #{:success :error :warn :neutral :default})
(s/def :button/text string?)

(s/def ::button-opts
  (s/keys :req-un [:button/type :button/text]))

(defn button [opts]
  (if-not (s/valid? ::button-opts opts)
    (do (js/console.error "Failed to render button; bad props" (s/explain-data ::button-opts opts)))
    (let [{:keys [text type]} opts
          styles (get-button-styles type)]
      [:button {:type "button"
                :style styles} text])))

seancorfield03:07:27

The spec is named by the qualified keyword. So ::type would expand to :whatever.ns/type and be referred to that way, and :button/type is just another keyword.

samueldev03:07:28

okay makes sense :thumbsup:

seancorfield03:07:45

Also ::foo/bar expands to whatever the alias foo refers to.

seancorfield03:07:40

So if you have (:require [quux.thing :as foo]) then ::foo/bar means :quux.thing/bar

seancorfield03:07:00

(and the same for aliases created via alias)

seancorfield03:07:26

user=> (alias 'foo (create-ns 'quux.thing))
nil
user=> ::foo/bar
:quux.thing/bar
user=>

samueldev04:07:43

I also just discovered Orchestra, to allow me to define my function alongside the args + return specs in one spot

andre.stylianos08:07:55

There's also https://github.com/gnl/ghostwheel It can optionally use orchestra under the hood for validation, but it also a has a more pleasant api with the >defn macro (in my opinion) and comes with some addtional features

samueldev04:07:45

very convenient šŸ™‚

samueldev04:07:53

now to make it work with clojurescript :thinking_face:

lmergen06:07:14

@samueldev i use orchestra + expound extensively -- defn+spec about half my functions, it's a great way to improve error checking

dominicm07:07:46

What is the idiomatic way to process a list of things with an f of side-effects, but grabbing the result? mapv feels like cheating.

delaguardo08:07:12

You can use (into '() (map ...))

delaguardo08:07:39

but mapv is also idiomatic in that case, imho

dominicm08:07:03

I feel like I want run! but with results.

reborg08:07:24

(doall (map f coll)) seems better to me. At least when I see it I know why. mapv could be for any other reasons

šŸ‘ 4
noisesmith13:07:04

(into '() ...) reverses the input

noisesmith13:07:29

also '() can always be replaced with ()

noisesmith13:07:36

vectors are always better than seqs if you don't need laziness, because they can give you a seq any time you like

Charles Fourdrignier09:07:52

I work on OpenCV code. Being based on C++ code, it implies a lot of ā€œoutā€ mutable parameters. An example, here edges is the result of applying ā€œCannyā€ on src and must be instanciated before. (Imgproc/Canny src edges 100 500) I want to chain functions in a Clojure classic way.

(->> image
    convert-gray
    find-edges
    (draw-mat panel))
So I wrap functions like this.
(defn find-edges [src]
    (let [edges (Mat.)]
        (Imgproc/Canny src edges 100 500)
        edges))
Is there a better way of doing this ?

reborg09:07:00

check doto

Charles Fourdrignier09:07:36

@reborg I did but doto put the variable in front of the given arguments or in my case itā€™s at second place. Do you suggest I write a macro doto-like ? I always forgot the macro solution.

reborg11:07:46

I see @charles.fourdrignier. Indeed a macro is straightforward:

(defmacro doto-> [expr name & forms]
  `(let [~name ~expr] ~@forms ~name))

(doto-> (Mat.) %%
  (Imgproc/Canny src %% 100 500)
  (Imgproc/ConvertGray src %%))

delaguardo11:07:07

There is also a little trick for doto

(doto (Math.)
  (#(Imgproc/Canny src % 100 500))
  (#(Imgproc/ConvertGray src %)))

šŸ‘ 8
kwladyka11:07:13

In the context of https://www.reddit.com/r/Clojure/comments/63le75/clojure_and_graphql/ what about security (get data of different user) and alternatives?

noisesmith13:07:47

as far as I know security is your job - whatever you plug into the graphql layer

orestis14:07:16

Thereā€™s also #graphql for more specific discussion.

roklenarcic13:07:27

I wish there was something like partial, but shorter in name and more like #() in terms allowing gaps. Something like (p* + 1 _ 3 _)

roklenarcic13:07:39

which would generate a function of two parameters

noisesmith13:07:42

in scheme this is cut

noisesmith13:07:12

#(+ 1 %1 3 %2) is nearly that

roklenarcic13:07:13

yeah but can't be nested

roklenarcic13:07:43

Also numbering arguments is kinda annoying

noisesmith13:07:51

shouldn't be hard to implement cut, which is your idea with <> instead of _ to denote args

noisesmith13:07:26

imho _ is misleading because idiomatically it means "I have to accept this input but I ignore it on purpose" in lisps

delaguardo13:07:49

@roklenarcic maybe here you can find what you are looking for - https://github.com/rplevy/swiss-arrows

noisesmith13:07:12

`-<>` in swiss-arrows plays a similar role, but for one value only. It is deprecated because clojure implemented the same thing (plus the ability to pick your placeholder) with the name as->

noisesmith13:07:41

no - -<> does other weird things, never mind

roklenarcic13:07:21

you're right, -<> is a lot like as->

roklenarcic13:07:29

except it also has a default position

roklenarcic13:07:42

and the package offers some-<> as well

roklenarcic13:07:10

maybe it would be nice to have some-as-> in clojure.core as well

hyankov15:07:47

Recently there was a github twitter thread about threading macros. @borkdude shared this as one of the outcomes: https://gist.github.com/borkdude/af4978cd9849357aed25144369fe007c

šŸ‘ 4
b2berry14:07:25

Suppose I have a map defined in a namespace :foo.bar/thing but I want to dynamically reference thing given only a keyword version :thing. So I need to convert :thing to :foo.bar/thing and be returned its value.

b2berry14:07:25

I know with functions this is done with a reader #' but I canā€™t seem to sort this for a plain-ol-def. Iā€™ve tried something like <quote>:foo.bar/~(name :thing) as a naive first pass but thatā€™s not the ticket

b2berry14:07:31

Where <quote> is a back-tic, donā€™t know how to escape those characters in slack.

Janet A. Carr14:07:40

Couldnā€™t you dispatch on a multimethod?

b2berry14:07:44

Hmmm thatā€™s an idea. I was hoping to just pass around ā€œpathsā€ and dynamically look up these models (maps) where the namespace of the maps isnā€™t necessary to encode in the path.

b2berry14:07:24

Regardless of the design Iā€™ve become a little curious how to dynmically resolve namespaces lin the same way a #' works for resolving fn vars

noisesmith14:07:08

#' is used to access a var given symbol resolution (instead of resolving all the way to the value in the var, as is normal)

noisesmith14:07:40

so for any foo/bar #'foo/bar gives you the var holding that value, instead of the value

noisesmith14:07:52

and foo is looked up using the namespace aliases in effect

b2berry14:07:45

Given :thing how would you go about dynamically resolving :foo.bar/thingā€™s value ?

noisesmith14:07:09

:foo.bar/thing has itself as a value

noisesmith14:07:22

keywords are self-resolving, they never point to some other thing

noisesmith14:07:28

do you want foo.bar/thing ?

b2berry14:07:43

Sorry, there is additional context above.. let me reframe the question

noisesmith14:07:21

anyway, the simplest is (defn foo-bar [k] (keyword "foo.bar" (name k))

noisesmith14:07:27

if you know it's in foo.bar

noisesmith14:07:41

(that just gives you a keyword)

b2berry14:07:47

There exists def thing {:fizz "buzz"} in namespace :foo.bar given :thing Iā€™d like to dynamically retrieve the value {:fizz "buzz"}

noisesmith14:07:00

there is no :foo.bar namespace

noisesmith14:07:09

namespaces don't get named by keywords

noisesmith14:07:24

asking about keywords here is totally confusing the issue

b2berry14:07:28

Yes Iā€™m aware of that.

b2berry14:07:14

The keyword is simply a reference in a path and I need to resolve itā€™s value which happens to exist elsewhere in the app (in another namespace)

b2berry14:07:19

Iā€™m probably just doing a poor job framing the question, Iā€™m not actually confused about how keywords are resolved or used.

noisesmith14:07:16

user=> (resolve (symbol (name :clojure.string) (name :join)))
#'clojure.string/join

noisesmith14:07:48

> in namespace :foo.bar made me think you didn't understand namespaces

b2berry14:07:25

I have a path in the shape of [:entity :attribute] and in this example would be [:thing :fizz] and I need to resolve that to the value "buzz" but the path is in namespace a and knows nothing about where thing is defined.

b2berry14:07:49

Oh interesting let me play with what youā€™ve provided.

noisesmith14:07:03

this stuff is much easier when you don't try to treat namespaces as containers btw

b2berry14:07:15

Gah tell me about it! šŸ˜„

noisesmith14:07:17

:a isn't a namespace!

noisesmith14:07:21

you did it again

b2berry14:07:44

Yes sorry, fixed, itā€™s just a matter of muscle memory getting in the way

noisesmith14:07:09

anyway, if you just put a hash-map in a def, this kind of lookup becomes much easier

noisesmith14:07:39

especially if you start adding things at runtime, don't create new def from runtime input, add keys to a hashmap in an atom

noisesmith14:07:44

a good rule of thumb is anything that uses resolve is hacky and halfway broken

taylor19:07:05

do you mind expanding on this? I've seen some code recently that resolves symbols provided via config, and I'm wondering about pitfalls

noisesmith20:07:43

with regular code, the compiler can tell you if any symbol you are implicitly resolving doesn't exist with resolve, this becomes a runtime error when your function is called this can be OK if you know you are using resolve in a limited way on startup for eg. dep injection, but using resolve should be a special case with an explicit rationale (in a code review a dev should be able to explain exactly why they are using resolve and not something more mundane), since resolve can make code very brittle

šŸ‘ 4
b2berry14:07:29

Yeah Iā€™m thinking instead of trying to force this to work Iā€™m just going to backup and refactor this part of the app to get myself out of this corner.

b2berry14:07:59

Iā€™m fairly sure Iā€™ve still not adequately painted a picture of what Iā€™m trying to solve here, which is a smell enough in itself.

b2berry14:07:26

If code canā€™t be easily described, itā€™s probably too complicated/broken and needs to be rethought anyway.

b2berry14:07:48

Anyway, thanks for giving it a go šŸ™‚ much appreciated.

hyankov15:07:47
replied to a thread:or `as-some-&gt;`

Recently there was a github twitter thread about threading macros. @borkdude shared this as one of the outcomes: https://gist.github.com/borkdude/af4978cd9849357aed25144369fe007c

šŸ‘ 4
borkdude15:07:36

@hyankov In hindsight I think this is better: https://twitter.com/borkdude/status/1011164750523326465 No need for hybrids that way.

šŸ‘ 8
hyankov15:07:36

@borkdude Thanks, I remember the morning I saw this tweet. Playing in the REPL trying to disprove you (unsuccessfully) was a great start of the day šŸ™‚

noisesmith15:07:57

also it's good to remember how dumb form rewriting macros are - they really don't care what's in them

user=> (->> (+ a b) (let [a 10 b 32]))
42

exupero16:07:19

That more or less gives you Haskellā€™s where syntax.

souenzzo17:07:28

(as-> [] % (conj % 33) (map inc %)) is cool to.

seancorfield17:07:27

as-> is intended for use inside ->, not standalone šŸ™‚

orestis17:07:28

It seems within a defn, I canā€™t require something and then immediate use it, right?

noisesmith18:07:46

not without hacky things like resolve

orestis18:07:37

Figures. My use case is to be able to use the swing inspector as needed in the repl, without having the require left at the top level. Right now Iā€™ve just put everything in a comment block and I require as a first step. I can live with it.

emccue19:07:28

What are people's opinions on checked exceptions or enum-like result values

val_waeselynck20:07:28

It's hard to give relevant opinions without knowing more context - like objectives and constraints.

emccue00:07:34

I guess in general I wonder about how best to handle exceptions at the interface level. In clojure its all dynamic so I just document and move on, but in java stuff like JSONException is checked so I need to declare, rethrow, or use sneakythrows. It always feels like its bad to expose exceptions like that other parts of the code, but I can't put my finger on why, nor do I know the prevailing opinions.

emccue19:07:57

I am venturing into javaland

elliot21:07:19

First time back in a while using clj instead of lein. Trying to do the CLJS quickstart but getting cp: PREFIX/example-deps.edn: No such file or directory. Any pointers? (set up on via from brew install clojure)

elliot21:07:37

(canā€™t seem to find whatā€™s looking for that example-deps.edn file, not sure what module/repo the call is coming from)

elliot21:07:51

seems to be something up in the brew-installer itself: https://github.com/clojure/brew-install

Alex Miller (Clojure team)21:07:14

Sounds weird. I have not heard anyone having issues. Did you brew doctor?

Alex Miller (Clojure team)21:07:53

I would do that, then uninstall completely, then install again

elliot21:07:08

in clojure, thereā€™s a literal install_dir=PREFIX

Alex Miller (Clojure team)21:07:21

That gets replaced during install

elliot21:07:32

which seems like it was supposed to be gsubbed

Alex Miller (Clojure team)21:07:38

Or at least, when it works it does

elliot21:07:06

interesting, should be able to hack it, will post if I figure out why that didnā€™t get subbed

elliot21:07:43

decent chance something is busted with my ruby/homebrew setup, hopefully not a problem for anybody else, sorry for the chaos/false alarm.