Fork me on GitHub
#clojure
<
2019-10-10
>
emccue00:10:39

@kenny It definitely is

emccue00:10:03

can you give an example of what you are wanting to do?

noisesmith00:10:46

you can write a macro that does something "on the fly" but given it's a macro, the fly is the time that the macro is expanded, which is part of compiling the code

noisesmith00:10:25

which means you can compile new code as part of your algorithm (aka eval) or find something else equivalent to what the macro does

noisesmith00:10:54

expect other devs to be very suspicious of code that uses eval

noisesmith00:10:49

also probably worth mentioning that RH has talked about the utility of late binding / dynamic extension, in the form of using multimethods or protocols instead of case / cond / match macros; if you do this his way the problem solves itself (but you lose the pretty match macro)

kenny00:10:44

Yeah, I need the things the match macro provides.

emccue00:10:22

slightly unrelated

emccue00:10:06

i was trying to use the tupelo library

emccue00:10:20

it pulls a lot of deps

emccue00:10:06

esp. considering i was just using it for map-keys

emccue00:10:20

are there any more lightweight util libs people use?

noisesmith00:10:25

it's actively maintained, and doesn't have a huge dep tree

noisesmith00:10:28

also the quality of the code itself is well above the average baseline for clojure libs last I checked

alfredx02:10:43

Wanted to have a look, but wonder why there is little in README.md and no wiki

alfredx02:10:00

Am I expected to read src or test?

noisesmith16:10:21

this might be a clue https://github.com/clj-commons/useful/blob/master/build-docs.sh - it uses a gh-pages branch of the repo via codox

noisesmith16:10:33

"lein doc" builds the doc files

noisesmith16:10:24

here's the branch they propagate via that script: https://github.com/clj-commons/useful/tree/gh-pages - I assume there's an index file

noisesmith16:10:37

I wonder if there's a way to treat all that html as a website - not through github itself it seems

noisesmith16:10:03

you could check out the code, and switch to that branch, and browse locally I guess

seancorfield02:10:51

@emccue I think map-keys and map-vals are under consideration for core at some point (maybe 1.11)? I think I read that somewhere.

Alex Miller (Clojure team)03:10:34

yes, or at least map-vals, although probably not under that name

emccue02:10:11

thats cool to know

seancorfield02:10:33

If you are using CLI/`deps.edn` and want those functions with no deps, use https://gist.github.com/seancorfield/6e8dd10799e9cc7527da5510c739e52f

seancorfield02:10:54

(yes, you can have a multi-file gist as a git dependency in deps.edn! Isn't that cool?)

emccue03:10:03

unrelated and possibly simple question

emccue03:10:29

i can't seem to find a (test.check.generators/repeatedly call-me)

emccue03:10:43

not sure if that is on pourpose

emccue03:10:21

keep calling a function like (repeatedly) was what i was looking for

Alex Miller (Clojure team)03:10:16

test.check generators are designed to be the only source of novelty in generated values (not arbitrary other functions) - this has several purposes. First, it allows the generator to use the same seed and generate the identical values. Second, it allows the generator to control how values are "shrunk" in the case of a failure to a simpler failing value.

4
seancorfield03:10:17

@emccue Depending on what you're trying to do, I suspect you want fmap over a known set of values?

emccue03:10:59

i have a function right now that just shuffles a coll and picks out values

emccue03:10:23

its not a blocker, i just wanted to confirm that its not there

emccue03:10:31

and i wasn't missing anything

emccue03:10:50

kinda curious how shrinking is done actually, so im going to read that

seancorfield03:10:43

There's a shuffle generator. So maybe fmap first over that?

emccue03:10:48

(defn random-tile
  "Generates a random tile"
  []
  (let [shuffled-ports (shuffle tile-ports)
        pairs (mapcat (juxt vec (comp vec reverse))
                      (partition 2 shuffled-ports))]
    (into {} pairs)))

emccue03:10:19

(def tile-generator
  (gen/fmap
    (fn [shuffled-ports]
      (let [pairs (mapcat (juxt vec (comp vec reverse))
                          (partition 2 shuffled-ports))]
        (into {} pairs)))
    (gen/shuffle tile-ports)))

(defn random-tile
  "Generates a random tile"
  []
  (gen/sample tile-generator 1))

emccue03:10:24

went from top to bottom

emccue03:10:30

its a wierd case so its okay

Alex Miller (Clojure team)03:10:06

if you can enumerate all possible tiles, I would just use (s/gen the-set-of-tiles)

emccue03:10:15

I think i can actually

emccue03:10:22

thats alot less hacky

Alex Miller (Clojure team)03:10:57

since all of the specs know how to generate already, it's often much easier to build a simple spec, and use its generator

Alex Miller (Clojure team)03:10:29

or use (s/gen (s/tuple ...)) then gen/fmap or gen/bind over that

Jakub Holý (HolyJak)06:10:25

Hello! I want to 1. extract a thing (a person) from a collection, 2. verify that it exists and passes a test (`:alive`), 3. bind it for future use. Is there a better way to do this (extraction + test yet returning the thing, not the boolean result of the test fn) then

(if-let [jon ((fn [person] (when (:alive person) person))
              (get people :jon))]
  jon)
? Thank you! I could also
(if-let [[jon] (filter :alive
                  [(get people :jon)]
  jon)
but it feels wrong to have to wrap and unwrap [] to do this

noisesmith16:10:24

destructuring inside if-let doesn't work like you expect

Jakub Holý (HolyJak)06:10:42

It doesn't? Could you be so kind and explain? Thank you!

noisesmith16:10:47

user=> (if-let [[x] []] [:yes x] :no)
[:yes nil]

noisesmith16:10:29

the if-let uses the truthy branch if the entire input is truthy (`[]` is truthy here) and doesn't care if the actual binding is nil

noisesmith16:10:42

closer matching your example:

user=> (if-let [[x] (filter even? [1 3 5])] [:yes x] :no)
[:yes nil]

jaihindhreddy07:10:56

You could do

(when (get-in people [:jon :alive])
  (get people :jon))

Jakub Holý (HolyJak)07:10:51

Well, the goal is to only get once.

jaihindhreddy09:10:17

(let [jon (get people :jon)]
  (when (and jon (:alive jon))
    jon))
I am using it only once here 🙃

jaihindhreddy09:10:25

In all seriousness, I feel like we are complecting checking whether a particular person is alive, with getting them from a collection here. Maybe the filter is a separate step in a collection operation.

(-> (filter #(:alive (val %)) people) 
    (keep [:jon :jim :jack :jones]))

Jakub Holý (HolyJak)06:10:13

I guess I will go with if inside let. I'd prefer less nesting but there seems to be no nice way. Yes, I'm doing two things but in my mind it is one - "give me thing X that satisfies condition C"

👍 4
reefersleep11:10:15

(when-let [jon (some->> people
                        (filter (fn [[person-name {:keys [alive]}]]
                                  (and alive
                                       (= :jon person-name))))
                        (first)
                        (second))]
  jon)

reefersleep11:10:19

Not much different from the other suggestions, and no closer to what you want, I guess. 😕

👍 4
dominicm09:10:58

If I have an instance of a class, and I'd like that instance to extend a protocol without affecting all instances, is that possible?

jaihindhreddy09:10:56

Why not close over your instance with a reify, does this thing also need to behave as your original instance, in all other contexts?

jaihindhreddy09:10:25

The problem with external extension or extension via metadata is, if the instance at hand already implements the protocol via deftype or defrecord, then we cannot override it with metadata or extend. The only way to override that is via reify.

jaihindhreddy09:10:08

As yuhan pointed out, this is the order in which Protocol implementations are searched:

Protocol implementations are checked first for direct definitions (defrecord, deftype, reify), then metadata definitions, then external extensions (extend, extend-type, extend-protocol).

jaihindhreddy09:10:12

If the class of the instance at hand doesn't already implement the protocol, I wonder why you care if your extension applies for all instances. Is this in library code or application code?

dominicm10:10:54

I'd like it to behave like the original instance. This would be in library code, and I don't think a library can claim ownership of the class in this case. It's an integration library.

👍 4
4
yuhan09:10:11

You probably could via metadata if the protocol supports it: https://clojure.org/reference/protocols#_extend_via_metadata

dominicm09:10:33

Do all objects support metadata? I didn't think they did

borkdude09:10:03

only if they implement IMeta

roklenarcic10:10:59

Is it possible to make alias in deps that enables other aliases. So for instance if I have a tool like Cloverage which is run through alias cloverage, I'd like to always run it with dev and test alias enabled

chadhs11:10:17

not sure the best channel to ask this in, but is anyone interacting with stripe from their clojure project? looks like stripe-clojure listed under “other” on the stripe site hasn’t been touched in ~5years

jumar12:10:08

we just use clj-http

vemv15:10:36

https://github.com/clojurewerkz/elephant seems far more active than that, and from good source (clojurewerkz)

chadhs10:10:43

thanks for the replies folks

chadhs10:10:35

@U06BE1L6T that realization kind of dawned on me while i was taking my dog outside later that morning. look at their curl examples and just use clj-http. that’s what i’m using for other services i’m interacting with as well. thanks!

👍 4
emccue16:10:07

@dominicm I've seen a to-x sort of pattern for this recently

emccue16:10:53

(defprotocol Bird
  (fly* [_]))

(defprotocol ToBird
  (to-bird [_]))

(defn fly [birdlike]
  (if (satisfies? Bird birdlike)
    (fly* birdlike)
    (fly* (to-bird birdlike))))

emccue16:10:52

so for stuff you dont want to "hard" claim you can impl to-bird

emccue16:10:35

which would let the user override it - with the downside of overriding your definition

emccue16:10:57

but you can also straight do a v-table like thing yourself

emccue16:10:40

and store a reference to a factory function for a reify under a specific type

emccue16:10:41

not sure if that helps in your specific case or not

noisesmith16:10:09

wouldn't that be simpler as a multimethod with to-bird baked into the bird impl?

☝️ 4
dominicm16:10:52

@emccue I don't think so, because another library could also soft claim this object type leading to a conflict.

emccue16:10:20

what exactly is the type in this case

dominicm16:10:57

java DataSource is one example of something it might be.

emccue16:10:06

you can keep registering proxys or the specific type behind a reify

emccue16:10:27

(defn make-your-datasource [existing]
  (let [impl (reify DataSource ...)]
    (do (extend-type (type impl) ...)
      impl))))

emccue16:10:35

but thats... dodgey

dominicm16:10:56

Yeah, agree 🙂

ghadi16:10:14

What was the original question?

emccue16:10:24

what ways would the datasources you produce be different from other data sources>

noisesmith16:10:24

oh I missed that, and that's weird

dominicm16:10:37

@emccue consider something like component/integrant. You may start by just calling (.connect) or you might do (.connect) (.migrate) or any other number of things. Maybe if you're returning a data source for mysql, you need some specific contextual setup.

noisesmith16:10:43

I'd make a new class (eg. proxy) that delegates to your instance for everything

emccue16:10:31

wouldnt they have a more specific class they could customize on then?

emccue16:10:51

(extend-type DataSource ...)
(extend-type MySqlConnClass ...)

emccue17:10:09

to my knowledge the inheritance relationship still works

hiredman17:10:08

it sounds like this is an attempt to work around the general wisdom for protocols of "only extend a protocol to a type if you own the type or the protocol", the idea being "ok, even if I don't own the type, I own this instance of the type, so I want to extend the protocol to just this instance"

👍 4
emccue17:10:10

(defprotocol A (a [_]))

(extend-type java.util.List
  A
  (a [_] 1))

(extend-type java.util.ArrayList
  A
  (a [_] 2))

(a (java.util.ArrayList.))
(a (java.util.LinkedList.))

dominicm17:10:28

@hiredman that's exactly what I was thinking about.

ghadi17:10:35

Seems like the protocol is at the wrong layer - wrap the datasource in a map and extend by metadata

hiredman17:10:49

I would just create a type I own

ghadi17:10:55

Component supports extension by metadata IIRC

👍 4
4
dominicm17:10:16

It does 🙂

noisesmith17:10:22

I think a proxy that delegates to your instance is cleaner - it's a singleton instance of a class by design, or you can make a new type if you need multiple instances

hiredman17:10:51

but proxy is terrible

emccue17:10:00

well, a deftype you own works

ghadi17:10:23

But you want data not objects generally

emccue17:10:31

in this case no

emccue17:10:55

its a connection to a thing - its probably being used, not inspected or passed over the wire

ghadi17:10:00

the connection goes in the map

ghadi17:10:21

If the protocol is component/start stop

dominicm17:10:31

> Seems like the protocol is at the wrong layer - wrap the datasource in a map and extend by metadata This is pretty much what I was thinking. Eschewing the protocol altogether though. Just use a map and define the "methods" as key->var references.

emccue17:10:06

That feels very weird in the context of clojure

emccue17:10:32

Like, that's basically how you have to do polymorphism in something like C

noisesmith17:10:59

I think it's an intrinsically place-oriented problem so a good solution will be place-oriented

4
emccue17:10:19

I'm not quite understanding the reluctance to making a new type for this

emccue17:10:03

Extension by metadata is a relatively new thing, but making a defrecord/deftype will definitely work

emccue17:10:04

Preserving the non protocol methods requires a passthrough impl, but it's not terrible

ghadi17:10:06

because wrapper types have to wrap all the functionality