This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-03-24
Channels
- # beginners (108)
- # boot (16)
- # bristol-clojurians (1)
- # cider (20)
- # cljs-dev (167)
- # clojure (64)
- # clojure-greece (4)
- # clojure-hamburg (1)
- # clojure-russia (1)
- # clojure-uk (27)
- # clojurescript (235)
- # datomic (1)
- # devops (2)
- # fulcro (80)
- # graphql (6)
- # heroku (2)
- # jobs-discuss (1)
- # jobs-rus (2)
- # lein-figwheel (1)
- # lumo (2)
- # nyc (1)
- # off-topic (22)
- # portkey (4)
- # re-frame (44)
- # reagent (39)
- # ring-swagger (9)
- # shadow-cljs (90)
- # tools-deps (5)
- # vim (8)
- # yada (2)
is there a predicate that checks if a value is a long?
(or an integer)
basically i want a "not a bignum" checking function, given an integral value.
should I just do (not (= (type x) clojure.lang.BigInt))
?
https://clojuredocs.org/clojure.core/int_q might work for you
@dpsutton That doesn't catch long
Oh wait it does?
Thanks
I want the pre-conditions to somehow work even for the unspecified arguments. Using :or
in the hashmap destructuring seems wrong here, as it would 1) be duplicating the external stage-defaults
values, and 2) wouldn't work, because seed
would be different between the 2.
Is there a way to integrate lein test-refresh
results into an editor like NCrunch or Wallaby.js? At the moment I just have a floating terminal with lein test-refresh
running but I would prefer a more integrated approach with my editor.
@mfiano I don't understand what you mean by "pre-conditions to somehow work even for the unspecified arguments", could you give an example of what is not working?
@schmee What I mean is not all keyword arguments may be passed to the function call, so any of them could be nil and not pass
pre- and postconditions are meant for simple arg-checking so they're not very flexible
also, may I suggest that you use a map instead of keyword arguments? it has a couple of advantages, most notably it is easier to combine arguments from different sources with merge
, and it is easier to use with apply
No I haven't. Basically this function is part of the user API, so I need to do a bit of type-checking and allow arbitrary keyword arguments with default values that are merged in.
this is a good fit for spec: spec has support for optional keys in your hashmap, so they will only be checked if they are present
I'm very new. This is actually my first bit of code. I've been wrestling with this for 3 days
I'll check what spec is
in that case my suggestion is: don't overthink it. just write some if statements, or
s or whatever to get it working and then move on
I'm trying to port my Common Lisp library. There is a lot of idioms like this
Ok thanks
(ns foo.foo
(:require [clojure.spec.alpha :as s]
[clojure.spec.gen.alpha :as gen]))
(defn validate-height [{:keys [height extent] :as m}]
(if height
(>= height (+ (* extent 2)))
true))
(defn validate-width [{:keys [width extent]}]
(if width
(>= width (+ (* extent 2)))
true))
(s/def ::width (s/and pos-int? odd?))
(s/def ::height (s/and pos-int? odd?))
(s/def ::seed pos-int?)
(s/def ::density (s/double-in :min 0.1 :max 1.0))
(s/def ::meander-rate (s/double-in :min 0.0 :max 1.0))
(s/def ::door-rate (s/double-in :min 0.0 :max 1.0))
(s/def ::stage
(s/and
(s/keys :req-un [::seed ::density ::extent ::meander-rate ::door-rate]
:opt-un [::width ::heigt])
#(validate-height %)
#(validate-width %)))
(def stage-defaults
{:seed 12419
:density 0.65
:extent 5
:meander-rate 0.0
:door-rate 0.5})
(defn make-stage [options]
(s/valid? ::stage options))
;; or (s/explain ::stage options))
Hmm, I've never seen a lot of this stuff like the double colon keywords
if you're in namespace foo
, then ::bar
expands to foo/bar
. this is used to avoid name collisions when merging keys from different sources
so in this case ::width
expands to foo.foo/width
, and another namespace can define a ::width
spec but since they're qualified there's no risk of collision
Interesting.
So basically I shouldn't use preconditions for most things
I sort of liked the concision, but oh well
you could do something like this:
(defn make-stage [options]
{:pre [(s/valid? ::stage (merge stage-defaults options))]}
"do the thing")
what is :req-un and :opt-un?
the default in spec is to require namespaced keywords for the inputs as well, so if you use req
and opt
here it means that the input map must have keys like foo.foo/meander-rate
etc.
I don't understand what spec is doing at all from this code. I'll have to read about spec first I think
here's a very nice introduction: https://clojure.org/guides/spec#_getting_started
Ok, I'll read. Thanks for your help
Wow spec is very nice. Clojure just keeps on impressing me. Thanks so much
I can't understand how :opt or :opt-un affect validation. If I pass an argument not specified, it doesn't raise an error
that's a deliberate design in spec, all maps are open. you can only specify what is required, you can't forbid keys. opt
is just for documentation purposes. IIRC, all keys in the map are validated if they have a spec registered, regardless of whether they are specified in keys
or not.
Ok. So the only issue I now have is I'm forced to merge twice
in the pre-condition, and in a let binding for the body
in that case I'd just skip the :pre
and do the check in a conditional in the body that reuses the let binding
Actually, that's not the issue:
my parameters are destructured, because these will eventually map to command line arguments:
[& {:keys [width height seed density extent meander-rate door-rate] :as options}]
then i do (:pre [(s/explain ::stage options)]}
But the body needs all the merged map values...so I need to also destructure a let binding which seems repetitious
Only using seed so far in the let binding, but there will be more: https://gist.github.com/mfiano/b46007fdbc4ae59130b1bd132e91424f
Which is optional, so needs to be merged
here's how I would write that function:
(defn make-stage [user-options]
(let [{:keys [width height seed density extent meander-rate door-rate] :as options} (merge stage-defaults user-options)]
(if (not (s/valid? ::stage options))
(throw (ex-info "Invalid options" (s/explain-data ::stage options)))
(do
(rng/set-seed! seed)
"more stuff here"))))
note that I use a map instead of keyword arguments, since IMO those tend to complicate things for little benefit
somewhere in the http://clojure.org docs I read that outer user api functions should use keyword args and stuff not for the user should just be regular maps, so i was following that advice
This function here is going to eventually be the main entry point to be exposed for running the jar on the command line with those required and optional parameters. is that going to be a problem?
huh, that seems like outdated advice to me. Note that I'm not saying that they're bad and should never be used, just that my experience has been that it's easier and more flexible to use maps instead π
The use of keyword arguments has fallen in and out of fashion in the Clojure community over the years. They are now mostly used when presenting interfaces that people are expected to type at the REPL or the outermost layers of an API. In general, inner layers of the code find it easier to pass options as an explicit map.
not bad advice by any means, feel free to use whatever you like π my version uses maps cause I think it's easier when you're merging multiple arg maps
The only thing I dislike is how specs with an external dependency have to have a separate function, and then added in the aggregate. In your example at the top of this thread, it's disappointing that validate-height and validate-width are nearly the same, and that they are invoked in the aggregate ::stage spec, so the whole map is given as input and difficult to figure out the problem in the case of an exception.
yeah, it can make it harder to find errors for sure, but I'm not sure how it could be done differently. that the methods are nearly the same is just sloppy coding from my side, you could easily modify the function to take a parameter for the key and reuse that function for both cases π
Yeah I was able to translate your sloppy coding. No worries. On a side note those validation function are always truthy π
I have some other issues too, like re-validating a hashmap as keys are assoc
d to it later efficiently, but optimizations for another day I suppose.
Appreciate all your help. I am definitely having fun with spec
why is not possible to redef the clojure.core/+ ? do am I doing anything wrong ?
(alter-var-root (var +) (fn [f] -))
Clojure 1.9.0
+user=> (alter-var-root (var +) (fn [f] -))
#object[clojure.core$_ 0x502f1f4c "[email protected]"]
+user=> (map + [1 2] [3 4])
(-2 -2)
+user=> (+ 2 3)
5
i works as long as it's not compiled directly into a form, but passed as an arg
there's an optimization that prevents your change in a normal call
+user=> (apply + [2 3])
-1
not sure I follow. is var redefinition contingent on JVM optimizations? or do you mean inlined by the Clojure compiler?
but since the compiler is built with it you'd have to recompile the compiler for the core fns to see redefs of each other
oh, right, the direct linking thing?
I see @noisesmith, thank you too @schmee
You can use the slim classifier version for an uncompiled version of Clojure
What is a good way to define a "zero or positive integer" spec with s/def
? I don't really need s/or
's named tags here.
Oh very good
What about a set of at least 1 keyword symbol?
homogenous set
It has pretty much everything. Thank you