Fork me on GitHub
#beginners
<
2020-04-09
>
Bill Phillips02:04:42

So maybe this is a naïve question, but… Say I’ve registered a spec for my fn:

(s/fdef import-post!
  :args (s/cat :remote-post post-spec/remote-post)
  :ret  post-spec/intern-post)
Is there any way to use the definition here of the spec for :args within import-post! to validate my input?

Bill Phillips02:04:19

Or can this spec only be used to validate the whole contract, with both input and output?

noisesmith02:04:45

you can do this within :pre - there are examples of this in the clojure spec guide

seancorfield02:04:31

@jings.bill instrument only checks :args -- you're aware of that?

nick06:04:08

I've recently found a library called https://github.com/jeaye/orchestra > By default, clojure.spec will only instrument `:args`. This leaves out `:ret` and `:fn` from automatic validation; Orchestra checks all of them for you. Which seems to work - :ret parts are validated in my tests now. I'm wondering why clojure.spec doesn't have this. There must some good reason which I'm not aware of yet 🙂 Is using orchestra considered more like an antipattern?

seancorfield16:04:02

Conceptually, instrument is about checking calls -- that your code is passing the correct :args -- whereas check is about checking behavior, using generative testing: it generates random args per :args, calls the function, and then verifies that :fn and :ret hold true. So it's two separate types of checking. @U0113AVHL2W

nick16:04:00

Got it. Thanks Sean!

seancorfield16:04:20

I see Andy gave you a good, long answer in another thread and linked to both the Spec FAQ and to Rich's conversation on the topic! I should have caught up with the main channel first I guess 🙂

nick16:04:54

np always glad to hear from you 🙂

seancorfield02:04:02

check is what exercises :ret (and :fn).

Bill Phillips02:04:04

I am not aware of that

noisesmith02:04:52

be aware that if you instrument your function, and one of your args is specced as a function, random data will be generated and passed to that function so don't do this with side effecting function args

seancorfield02:04:55

Also "validate" is a bit imprecise: to me, that means a production run-time validation of data and appropriate error handling on failure 🙂

Bill Phillips02:04:59

The spec guide shows :pre, but: (1) doesn’t illustrate referring to :args within the fdef, and (2) I asked about that and apparently it’s not a recommended practice?

seancorfield02:04:25

https://corfield.org/blog/2019/09/13/using-spec/ talks about various ways to use Spec in dev/test/production code.

Bill Phillips02:04:28

Yeah, that’s what I’m referring to, Sean. I’ve got a function that currently falls down on bad input, and I’d like a clear failure

noisesmith02:04:50

IMHO that's what :pre is for

noisesmith02:04:05

don't instrument in prod

seancorfield02:04:23

:pre and instrument are for dev/test really, not production -- for a lot of people -- because they're about assertions

noisesmith02:04:24

fdef is for instrumenting as I understand it

didibus02:04:29

I'd say for things you want to validate in prod, just use s/valid?

Bill Phillips02:04:31

Yeah, instrument seemed like the wrong tool

seancorfield02:04:58

Yup. If you want this in production use s/valid? or s/conform and have an explicit handler for invalid data.

Bill Phillips02:04:25

I’ve written this:

(defn import-post!
  [remote-post]
  (if (not (s/valid? (s/cat :remote-post post-spec/remote-post) [remote-post]))
    (s/explain (s/cat :remote-post post-spec/remote-post) [remote-post])
    ...
…setting aside the obvious repetition, the spec (s/cat :remote-post post-spec/remote-post) is precisely what I’ve written in :args in my fspec

didibus02:04:13

In theory, it would go: You've run st/check on all your functions, you've instrumented your code in devo while doing manual testing and when running your unit and integ tests. So you know everything works. But, there are some things that depend on runtime input, like input from a user, an API, a file, the DB, etc. Since there's no way to know whatever gives you this input won't give you garbage, for that, you add an explicit s/valid? or s/conform.

didibus02:04:36

That looks good to me, though you'd probably want to s/def that spec so you're not repeating it like you said

didibus02:04:12

I don't like s/assert as much, because the rules around how to enable/disable it confuse me too much 😛

didibus02:04:55

Also generally I like to control in prod the error returned or thrown with more granularity, but yea, it is also an option\

Bill Phillips02:04:39

How would I use s/def here? I thought it was used to wire namespaced keys to data specs?

didibus02:04:35

You already have a spec for it, so no, that's wrong

didibus02:04:00

You want:

(defn import-post!
  [remote-post]
  (if (not (s/valid? :post-spec/remote-post remote-post))
    (s/explain :post-spec/remote-post remote-post)
    ...

didibus02:04:34

sorry, edited it

Bill Phillips02:04:24

is there any particular reason this isn’t a crazy idea:

(defn valid-against-spec? [spec args]
  (if-not (s/valid? spec args)
    (s/explain spec args)
    true))

(let [args-spec (s/cat :remote-post post-spec/remote-post)]
  (defn import-post!
    [remote-post]
    (when (valid-against-spec? args-spec [remote-post])
      ...))

  (s/fdef import-post!
    :args args-spec
    :ret  post-spec/intern-post))

Bill Phillips02:04:31

apart from being kind of ugly and poorly named

didibus02:04:50

Hum..., I think s/fdef is a macro no? Can it capture the value of the let like that I'm not sure

didibus02:04:41

What's your hesitation for doing:

(defn valid-against-spec? [spec args]
  (if-not (s/valid? spec args)
    (s/explain spec args)
    true))

(defn import-post!
    [remote-post]
    (when (valid-against-spec? :post-spec/intern-post remote-post)
      ...))

  (s/fdef import-post!
    :args (s/cat :remote-post :post-spec/intern-post)
    :ret  post-spec/intern-post))

didibus02:04:09

It's like less code, and visually less ugly

noisesmith02:04:17

that when is always true

noisesmith02:04:14

oh it's a println error log, never mind

didibus03:04:14

If you really wanted to reuse the args spec of fdef, there's a way to get it from the fdef I think. Can't remember on top of my head though

didibus03:04:25

But still, to me those are different. You're trying to validate remote-post, and you already have a spec for it. The s/cat is not the spec of remote-post, it's the spec of the argument vector of the function, that's why you need to do the shenenigan of converting remote-postinto a vector before you validate it against the arg vector spec. So I just feel it's a roundabout way.

Alex Miller (Clojure team)03:04:48

If you s/get-spec the var symbol, that’s an fspec that supports keyword lookup of :args, :ret, :fn

👍 8
didibus03:04:48

Ah ya, that's how

Bill Phillips03:04:06

(s/explain (s/cat :remote-post :post-spec/intern-post) [remote-post]) doesn’t yield as nice an error message as (s/explain :post-spec/intern-post remote-post).

Bill Phillips03:04:46

b/c it’s not really validating the same thing: the former is validating the arg list, the latter is validating an individual argument by hand

Bill Phillips03:04:53

i’ll try that, alexmiller. i didn’t think to just (:args (s/get-spec 'symbol))

Bill Phillips03:04:31

the docs for what fdef registers and get-spec yields are opaque

nick06:04:08

I've recently found a library called https://github.com/jeaye/orchestra > By default, clojure.spec will only instrument `:args`. This leaves out `:ret` and `:fn` from automatic validation; Orchestra checks all of them for you. Which seems to work - :ret parts are validated in my tests now. I'm wondering why clojure.spec doesn't have this. There must some good reason which I'm not aware of yet 🙂 Is using orchestra considered more like an antipattern?

andy.fingerhut06:04:58

There question is probably in an FAQ about spec somewhere -- it is definitely frequently asked.

🙏 4
nick06:04:47

Found it! Thank you

andy.fingerhut06:04:30

One of the reasons orchestra exists is because many people want return value spec checking on many functions during testing, despite spec not helping you do that, except when running tests specifically on that function

nick06:04:37

Not sure I understand. From what I see I can just use orchestra.spec.test/instrument and it checks all specs, no need to call instrument on every function.

andy.fingerhut06:04:21

If you do not use orchestra, only the instrument that comes with Clojure.spec, no ret value checking is performed. orchestra does do ret value checking

nick06:04:57

oh Got it

andy.fingerhut06:04:47

You asked why clojure.spec doesn't have this -- this is asked so often, I am sure someone has written an answer to "why?' somewhere, but not sure where at the moment

nick06:04:45

Found it on https://blog.taylorwood.io/2018/10/15/clojure-spec-faq.html "Q: Why doesn’t `instrument` check my function return value?"

andy.fingerhut06:04:54

That is a guessed reason by the author of that article, who did not write spec, nor apparently did they ask the author of spec

andy.fingerhut06:04:23

Here is an older answer to the question, plus a link to where Rich Hickey answered it: https://groups.google.com/forum/#!msg/clojure/JU6EmjtbRiQ/uND70kAFBgAJ

nick06:04:52

Thank you very much Andy!

andy.fingerhut06:04:07

Ah, and there is also a brief answer in http://clojure.org's faq list here: https://clojure.org/guides/faq#instrument_ret

andy.fingerhut06:04:40

As I said, enough people find that answer unsatisfying that orchestra helps you do what many people would like to

👍 4
Jim Newton07:04:30

Is there an idiom for using (try ...) something like the following.

(try (value-or-throw-exception)
    (catch ExceptionInfo e x1)
    (catch AnotherException e x2)
    (else-with v x3)
The idea is that if we catch a certain error then whatever x1 (or x2) evaluates to is the value returned from try . However, if no exception was thrown, then I want try to return x3 ; but important is that the else-with clause has access to the value returned from the first argument of try ?

andy.fingerhut07:04:27

I am not aware of something like that built into Clojure's try/catch. It is straightforward to bind the return value of the entire try expression to a let symbol, and then do further computation on that. The return value from the try could of course be written to make it clear whether an exception was caught by it or not, but that is not required behavior for a try block -- you would have to write its contents to ensure that.

andy.fingerhut07:04:24

There are a few libraries people have written to provide macros that provide additional features over built-in try , but I have not used them personally. slingshot is one such library: https://github.com/scgilardi/slingshot

Timur Latypoff07:04:25

(try (let [v (value-or-throw-exception)]
       x3)
    (catch ExceptionInfo e x1)
    (catch AnotherException e x2))
I guess 🙂

👍 8
Jim Newton07:04:03

Good suggestion Timur.

Jim Newton07:04:40

obvious when you look at it.

noisesmith15:04:31

another gotcha to bring up since this is #beginners : finally clause runs last, but is not the return value of the try form

Jim Newton07:04:01

Funny enough, one CL habit which is excessively hard to break is to use ~ it interpolate a value into a backquote. I almost always first type

`(a b ,c d)
rather than
`(a b ~c d)
to me the , syntax is really hard wired deep in my brain. Not complaining, just commenting about habits being hard to break.

andy.fingerhut07:04:05

This is one example that caused me to make that comment about American vs. British English -- so similar, yet so crucially different in a few ways that become automatic when using only one or the other for a long time 🙂

andy.fingerhut07:04:01

There is an early talk by Rich Hickey on Clojure, given to an audience of Common Lisp and Scheme users, where many questions are raised, and answers given, about why he designed some things to be explicitly different than those.

Jim Newton07:04:43

I understand the motivation, and agree with it. we want comma to be white space. its a good decision. I just need to get used to it.

Jim Newton08:04:40

is there some case where using the empty set #{} as a function and calling it with some value should return null ?

Jim Newton08:04:04

When debugging my program, this seems to happen, but I cannot reproduce it in the repl.

Jim Newton08:04:24

clojure-rte.core> (let [done #{}
                        pattern '(:* (:or Byte Integer Long Short))]
                    (done pattern))
nil
This returns nil as expected. But in this function: the println done prints #{}, and (done pattern) prints as null then I get an execution error.
(defn find-all-derivatives [pattern]
  (println (format "find-all-derivatives %s" pattern))
  (loop [to-do-patterns (list pattern)
         done #{}
         triples [] 
         ]
    (println (format "   to-do-patterns = %s" to-do-patterns))
    (println (format "   empty? = %s" (empty? to-do-patterns)))
    (println (format "   done = %s" done))
    (println (format "   triples = %s" triples))
    (if (empty? to-do-patterns)
      [ triples (seq done)]
      (let [pattern (first to-do-patterns)
            to-do-patterns (rest to-do-patterns)]
        (println (format "   pattern = %s" pattern))
        (println (format "   (type done) = %s" (type done)))
        (println (format "   (done pattern) = %s" (done pattern)))
        (if (done pattern)
          (recur to-do-patterns done triples)
          (letfn [(xx [[acc-triples acc-derivs] wrt-type]
                    (let [triples (derivatives-narrow pattern wrt-type)]
                      ;; triples is a seq of triples each triple is of the form [pattern wrt-type deriv]
                      [(concat acc-triples triples)
                       (concat acc-derivs (remove done (map (fn [s] (s 3)) triples)))]
                      )
                    )]
            (let [[new-triples new-derivatives]
                  (reduce xx [[] ()] (first-types pattern))]
              (recur (concat new-derivatives to-do-patterns)
                     (conj done pattern)
                     (concat triples new-triples)))))))))
prints the following
clojure-rte.core> (rte-compile '(:* int?))
find-all-derivatives (:* (:or Byte Integer Long Short))
   to-do-patterns = ((:* (:or Byte Integer Long Short)))
   empty? = false
   done = #{}
   triples = []
   pattern = (:* (:or Byte Integer Long Short))
   (type done) = class clojure.lang.PersistentHashSet
   (done pattern) = null
Execution error (IndexOutOfBoundsException) at clojure-rte.core/find-all-derivatives$xx$fn (form-init16272114045093011175.clj:794).
null
clojure-rte.core> 

Jim Newton08:04:05

I don't understand why (done pattern) returns null, but it turns out that's not the problem in my program. The problem was my call to (s 3) should be (s 2) to retrieve the 3'rd element from an array. 0-based...

jsn08:04:11

What do you not understand -- why it is printed as null and not as nil? That's because you're using format (that stringifies any of nil , 'null and "null" as "null", so perhaps it's not the best choice for debug prints?)

Jim Newton08:04:44

ah, it's just a printing issue.. cool cool cool

nick09:04:12

What's the simplest/most idiomatic way to check that stest/check is passed in deftest? (deftest scale-test (is (empty? (failures (stest/check common/scale)))))` So far I could only found a guy on Github with this boilerplate function in his test utils namespace: (defn failures "Get any failing results of stest/check" [check-results] (mapv (fn [{:keys [spec clojure.spec.test.check/ret sym failure] :as x}] [sym (-> x (update :spec s/describe) (dissoc :sym) ;; Dissoc the top level trace, leave the shrunken one (update stc-ret dissoc :result-data))]) (remove #(-> % stc-ret :result true?) check-results))) Is it the way to go? Can't find it in the core

Aron10:04:53

https://github.com/dakra/mui-templates/blob/master/src/mui_templates/main.cljs#L141 where is that keyword :> coming from? how can I find it out in general where these come from? It's impossible to google it

Aron11:04:11

is this reagent specific? Again, my real question is, how to find out what something like this does?

Aron11:04:36

I am grateful for the solution for the problem at hand, but would like to learn fishing, not just eat a fish 😄

joelsanchez11:04:47

you're passing a keyword (`:>`) to a library. that library is free to interpret that keyword as it pleases. the answer is you can't know just by looking at the data itself

eval-on-point12:04:09

It is reagent specific as far as I know, but may be used somewhere I haven't seen. I don't believe vanilla Hiccup supports that syntax. A good strategy for finding documentation on hard to google expressions might be searching the repos of the library that the expression belongs to

Aron12:04:16

Mithchell: I tried to search on github, but couldn't, I would have to clone the whole thing and do some kind of local search, but thanks I will keep this option in mind.

Aron12:04:24

@U5LPUJ7AP I am not quite sure if I understand you correctly. Where can I see that "I am passing" "to a library" this keyword, I only saw it as a function definition that was passed to react-dom, I still don't quite understand how it gets resolved to what the documentation linked by Mitchell is saying.

joelsanchez12:04:19

all of this is much simpler than you think. If I call a function with a keyword, that function is free to do whatever it pleases with it. it doesn't "resolve" to anything by itself. it doesn't mean anything. the library gives it meaning.

joelsanchez12:04:39

as you can see, ">" is hardcoded there to mean something, that's it

joelsanchez12:04:48

that code is basically "if the tag is equal to :> then do this special native component thing, otherwise proceed normally"

joelsanchez12:04:57

> Where can I see that "I am passing" "to a library" this keyword, in the example you link to, main-shell is a function that returns a nested data structure that contains that keyword (`:>)` somewhere inside of it, at some point this structure is given to reagent/render and it interprets this keyword

joelsanchez12:04:35

> as passed to react-dom,

joelsanchez12:04:47

it is passed to reagent.dom

joelsanchez12:04:00

(which is the library we just linked to)

Aron13:04:25

thanks for elaborating on this, it helps a lot! indeed I confused reagent.render with reactdom.render : ( So I didn't even check if reagent has it.

Jim Newton11:04:17

how can it choose an element of a set which I know is not empty, I don't care which one?

Jim Newton11:04:22

I tried the following: (some identity the-set) but it sometimes fails, for example (some identity #{false})

Jim Newton11:04:41

first that's good. I had been using the following: (reduce (fn [acc item] (reduced item)) identity #{false})

noisesmith15:04:14

another thing to consider: (rand-nth (seq s))

Jim Newton15:04:24

Is there a type in clojure which every object is a member of?

Jim Newton15:04:44

something like T or Any or Everything ????

andy.fingerhut15:04:23

The JVM has class Object, that all other objects are sub-classes of.

andy.fingerhut15:04:41

But I suspect you are asking about more than that, given your earlier questions about isa? behavior.

Jim Newton15:04:19

is the type of x a subtype of Object for every x ?

andy.fingerhut15:04:35

I am not sure if I know what you mean precisely by the word 'subtype'. I can say that (instance? java.lang.Object x) is true for every JVM object x , and Clojure does not provide a way to pass a JVM primitive type to that call.

Jim Newton15:04:39

I think it is the same to say, for any given x, is (instance Object x) guarnateed to return true? or is thes some exception? Like null or nil etc etc etc

Jim Newton15:04:15

Hmm I see that (instance? Object nil) returns false

andy.fingerhut15:04:30

right, you beat me to that, so good for trying it out in the REPL 🙂

Jim Newton15:04:50

So is there a type above Object?

andy.fingerhut15:04:38

Again, not sure what you mean by 'type' here, again because of my lack of familiarity with Clojure isa? . There is no more wide class in the JVM than Object

andy.fingerhut15:04:00

Clojure nil is Java null , which is not an instance/object of any kind. It is the lack of an instance/object of any kind.

Jim Newton15:04:23

(type nil) returns nil which other objects have nil as a type ? I'm supposing that every object either has type Object or type nil ????

andy.fingerhut15:04:50

user=> (type 5)
java.lang.Long

Jim Newton15:04:21

yes but (instance? Object 5) returns true

Jim Newton15:04:58

so 5 is an instance of type Object

andy.fingerhut15:04:02

because java.lang.Long is a subclass (perhaps through multiple other classes -- I do to recall off the top of my head, but Java docs make it quick to find out) of java.lang.Object

andy.fingerhut15:04:57

e.g. This Java doc page shows the class hierarchy of java.lang.Long : https://docs.oracle.com/javase/7/docs/api/java/lang/Long.html

Jim Newton15:04:53

my understanding is that Object is a supertype of every type. except apparently the type which nil is an instance of.

andy.fingerhut15:04:36

nil in the JVM is not an instance of any type, is I believe a correct statement.

andy.fingerhut15:04:49

Well, null is its name in Java source code.

Jim Newton15:04:20

but it seems I cannot pass null to a clojure function. for example (type null) triggers an error

Jim Newton15:04:00

BTW what's the relation of nil and null in clojure?

andy.fingerhut15:04:31

If you do (source type) in a REPL, you will see that it in some cases calls function class , and (source class) shows that it returns nil given nil, and otherwise whatever the JVM method getClass returns. Java docs for method getClass shows that it returns an object of type java.lang.Class

andy.fingerhut15:04:43

Java null is exactly Clojure nil

andy.fingerhut15:04:05

Except Java null was named first, of course.

andy.fingerhut15:04:42

null in Clojure is not a special name for anything - you can def it, for example, and it will by like def ing any other non-special name.

andy.fingerhut15:04:57

user=> (def null [1 2])
#'user/null
user=> null
[1 2]

Jim Newton15:04:30

really good to know. I had no idea. Sometimes things print as null.

andy.fingerhut15:04:38

user=> (def true [1 2])
Syntax error compiling def at (REPL:1:1).
First argument to def must be a Symbol
user=> (def nil [1 2])
Syntax error compiling def at (REPL:1:1).
First argument to def must be a Symbol

andy.fingerhut15:04:22

If some things print as null , that is likely because of the function being used to do the printing is based on a JVM method that prints it as the string "null"

andy.fingerhut15:04:52

That should never happen if the function being called claims to print data in a Clojure-readable way, but not all functions claim to do that.

andy.fingerhut15:04:24

In general, Clojure in many cases does not attempt to hide the existence of the JVM from you. Just as ClojureScript in many cases does not attempt to hide the JavaScript runtime engine from you.

andy.fingerhut15:04:57

Straightforward interop with the host runtime is a design goal, not an accident.

Jim Newton15:04:14

so what's the correct way to print something for debug without having it print as null?

Jim Newton15:04:38

(printf "%s\n" (seq #{})) prints a null

andy.fingerhut16:04:04

Quick tests show these print nil as "nil": print, println, pr, pr-str

Jim Newton16:04:45

but pprint doesn't allow string interpolation. right?

andy.fingerhut16:04:07

Not all of those will print in a way that clojure.core/read will read, by the way -- but they do all print nil as "nil"

andy.fingerhut16:04:24

None of those work like printf does, no.

Jim Newton16:04:33

If I want to do something like: (printf "x = %s\n" x)

Jim Newton16:04:40

is that correct? or is there a better way?

andy.fingerhut16:04:49

I mean, if you like printf, and just remember that null in its output is Java null, which is identical to Clojure nil, then you should be ok with printf.

andy.fingerhut16:04:21

(println "x =" x) is not interpolation, but gives similar results as your example printf call

Jim Newton16:04:35

yes you're right.

Jim Newton16:04:01

looks like (cl-format true "x=~A~%" x) prints it as nil

noisesmith16:04:19

pr-str might be useful to you here as well:

user=> (printf "%s\n" (pr-str nil))
nil

noisesmith16:04:24

(defn clj-printf [fmt & params] (apply printf fmt (map pr-str params))) - this also escapes strings though

andy.fingerhut16:04:40

Well, and clj-printf would not support %d %f etc. in format strings.

4
didibus16:04:49

There is no type any

didibus16:04:07

Object is the supertype of all objects

didibus16:04:39

But you have a bunch of primitive types likes int, array, long, and all and they don't have a common supertype

didibus16:04:54

And I think null isn't part of the type system. All types can have the value null, so that's how null is handled I think. Like null is part of all sets of possible values of all types

noisesmith16:04:11

I thought null was a property of a place not a type, and all places could be null

noisesmith16:04:36

the jvm "type system" isn't even trying to be particularly consistent

noisesmith16:04:29

beyond the OO conceit that all things inherit from Object (which is disregarded as desired for performance)

Alex Miller (Clojure team)17:04:41

In Java, there are primitives and objects (which includes arrays). All objects extend from Object. There is no "type" that includes both objects and primitives. null is part of the type system - there is a special null type and null is the only value of that type. The null type is a subtype of all other reference types (except the null type). (It's not a proper bottom type as I understand it as it has one special null value, not no values.)

👍 4
Alex Miller (Clojure team)17:04:52

the Java Language Spec is pretty readable about all this stuff and worth reading if you are diving into this stuff.

Jim Newton14:04:05

@U064X3EF3 which java primitives are values in clojure?

andy.fingerhut14:04:00

There are special situations where you can get Clojure to emit JVM byte code that uses Java primitive long and double values, and maybe also some others, but quite often boxed Long and Double values are used instead.

Alex Miller (Clojure team)14:04:13

boolean, char, long, and double are really the main ones Clojure uses (and typically does not use byte, short, int, or float), but as Andy said, those are all typically boxed into their object forms

Alex Miller (Clojure team)14:04:21

IFn, the primary function interface, will always take and return objects. type hinting is required to coax it into taking or returning primitive longs or doubles

Alex Miller (Clojure team)14:04:45

within the scope of a let or loop/recur (actually both supported by the same compiler code), the compiler plays some tricks to retain primitives if possible

Jim Newton16:04:03

@U064X3EF3, I'm not sure my question was clear. You implied earlier that java has Objects and primitives, but I see that 1.0 is an instance of type Object. (instance? Object 1.0) returns true.

Jim Newton16:04:42

So is there another clojure object which is not an Object, other than nil. (instance? Object nil) returns false.

noisesmith16:04:11

if 1.0 is at the top level of a normal form, it is not a primitive, it's the boxed Double

noisesmith16:04:48

the compiler can generate and use the primitive version where it can prove it's possible and knows it's useful, but that doesn't include arguments to instance?

noisesmith16:04:00

so literally a primitive can't be passed to the instance? function

OrdoFlammae20:04:15

Is there a function that just returns the element if it exists in a list? I know that I can do effectually the same thing with some, but I wanted to know if there's a core function that operates like

(defn is-in?
  [x lst]
  (some #(= x %) lst))

OrdoFlammae20:04:04

Sorry, I think my question was confusing. I wanted to know if I could test for the presence of an element in a list, not whether a predicate returns true.

OrdoFlammae20:04:14

Otherwise I'd just use some.

phronmophobic20:04:15

or (some = lst) . [edit: as pointed out below, this does not work]

OrdoFlammae20:04:28

Huh? I don't understand how that would work @U7RJTCH6J. Doesn't = need two parameters, not one?

noisesmith20:04:44

for all values except nil and false, #{x} acts as a function that returns x if provided x as an argument

noisesmith20:04:07

@U010H5U6SEM don't use contains? on lists or vectors, it won't do what you want

4
OrdoFlammae20:04:28

OK, that's fine, I'll just use some. I just wanted to know if there was a special function that tested for the presence of an element.

noisesmith20:04:27

(cmd)user=> (some #{:a} [:c :d :a :e :b])
:a
(cmd)user=> (some #{:a} [:c :d :e :b])
nil

❤️ 4
noisesmith20:04:29

regarding contains?

(ins)user=> (contains? '(:a :b :c) :b)
Execution error (IllegalArgumentException) at user/eval214 (REPL:1).
contains? not supported on type: clojure.lang.PersistentList
(cmd)user=> (contains? [:a :b :c] :b)
false
(ins)user=> (contains? [:a :b :c] 1)
true

andy.fingerhut20:04:12

The examples and comments on the http://ClojureDocs.org page for contains? looks pretty accurate to me.

noisesmith20:04:39

yes - I was warning against a suggestion offered above, since it's a common new user trap

OrdoFlammae20:04:11

When contains? is applied to a vector, it returns whether the vector contains a value at the location (which is assumed to be a number), since vectors can basically be thought of as maps that are keyed in by index value.