Fork me on GitHub
#clojure
<
2019-12-26
>
theophilusx04:12:53

I've noticed a common use of comp to use a function that is passed in as an argument. For example

theophilusx04:12:21

(comp f vals) v (f vals). Why the comp?

dorab04:12:24

Can you show an example ? From what you have here, (comp f vals) is not the same as (f vals) . Perhaps you meant apply? If vals is a list of (say) 3 values (1 2 3), and f takes 3 arguments, then (apply f vals) would be the same as (f 1 2 3).

theophilusx06:12:57

What I've sen is functions which use (comp f arg) where I would normally just do (f arg) or maybe (apply f args) and I wasn't sure why you would use comp. I've seen this more than once, so thought there must be a reason. Is there any situation you would use comp when you just have a single function to evaluate?

seancorfield07:12:15

@theophilusx That doesn't make sense. (comp f arg) is not the same as (f arg) nor is it the same as (apply f arg). Can you link to examples of functions that you think are using (comp f arg) in this way?

jaihindhreddy07:12:45

Unsure if this is the right channel to ask this question... I'm designing a microservice on AWS, and its artefact is an orchestration of a couple of SQS queues, 3 Lambdas, APIGW, among other things. How do I structure my code, and write my build program (invokable with cli-tools) so that it produces three artefacts, one per lambda? I'd like to stick to one git repo if possible. Some amount of domain logic will be shared b/w the three lambdas.

seancorfield07:12:12

@jaihindhreddy What sort of artifacts do you want to build? Basic JVM uberjars? Is the JVM/Clojure startup time acceptable for Lambda for you?

seancorfield07:12:02

Assuming uberjars are fine, you're talking about a monorepo with multiple subprojects -- which is what we have at work -- and we build uberjars with depstar / CLI / deps.edn.

seancorfield07:12:49

Each subproject has its own deps.edn file and treats the other subprojects as :local/root dependencies (via ../<subproject> paths) as needed.

seancorfield07:12:29

Since depstar builds the requested JAR/uberjar based on the classpath, you would just cd into each lambda subproject, and build the JAR based on the classpath created from the deps.edn file, just as if you were running the lambda locally (but running hf.depstar.uberjar as you main namespace instead of whatever is in the lambda code).

seancorfield07:12:47

Does that help?

jaihindhreddy07:12:44

At least one of those is latency sensitive, but doesn't do much computation, so I'm thinking Graal Native image.

jaihindhreddy07:12:13

Never tried Graal though, and not sure how it works with Clojure.

jaihindhreddy07:12:26

But yeah, the monorepo route makes sense.

seancorfield07:12:58

There's a #graalvm channel so you can check that out. There are some restrictions that go with it (can't use Spec, for example, at the moment, and certain "dynamic" things) but startup times are awesome.

theophilusx08:12:13

@seancorfield Sorry, typo on my part - should have been ((comp f) v). One example is (defn process-map [f m] (into {} (map (fn [[k v]] [k ((comp f) v)]) m))) which I think is just poor code, but wasn't sure. I can think of a couple of alternative ways to do the above which are shorter and I think clearer. However, I saw this ((comp f) v) construct a couple of times and was wondering if there is something subtle I'm not understanding

p-himik08:12:12

No reason whatsoever since (comp f) is just f. Probably a leftover code that used to be something like (comp f g).

theophilusx08:12:01

@p-himik Thanks. That was my suspicion, but thought I'd check

Gulli11:12:09

I've seen this a few times, people asking how to build several services together because of shared code. Wondering if this is common to Clojure (I'm a Clojure newbie). What I would do in Java though is, isolating that code to it's own project and build that as a dependency for the other services. You would also be able to have independent build routines for each then.

solf11:12:24

I don't know if it's more or less common in Clojure than in other languages. Having multiple projects, potentially multiple git repos, instead of one single big codebase is an open question IMO. I've had a very annoying experience in a previous work where we had many common libraries, each living in their own git repo, each used in multiple projects.

Alper Cugun11:12:21

There’s a lot of folklore around it but once you’ve worked in a monorepo, you probably won’t want to go back.

kwladyka14:12:42

> many common libraries It sounds wrong. Maybe you made libraries from things which are used only in 1 project or split them too much. Like a micro libraries ;)

solf11:12:16

I'm sure if we were a bigger team I would have found advantages to that, but as it stand, our small 3-man team dev spent a lot of time time doing commit -> pr -> release -> update dependencies of all relevant projects whenever we needed to make a change in one of our common libs. Which was often. Working there I dreamed at night of working with a monorepo.

💯 1
kwladyka14:12:21

you could use ci/cd for this purpose. If detect new version of X, then update dependency in project and test it with new dependencies. If pass make a auto commit with new ver. of your library.

kwladyka14:12:05

This is probably the way which I would choose if I would find myself always bump version in each project after release.

kwladyka14:12:31

But for sure not keeping everything in one repository. It always end with a mess and complexity made by developers.

Gulli11:12:18

Yes, context definitely matters. I don't think he was talking about a monolith though (one big code base). But they can also be so small that having them in the same project could make sense (has it's pros & const). Also Alper also brings up monorepo, which many companies seem to be using now (never used it myself). You could also group small utility projects into a super pom, then use it as a dependency in your services. It's maybe just a personal preference, but I feel that edge/outward facing services should be as independent as possible.

tvalerio16:12:41

anyone could give me a help with specs?

tvalerio16:12:55

I have defined these predicates and spec but when I execute the above line in repl I get false as result

(invalid_transfer? {:uuid_account "745286b0-24d3-4b17-ab24-d1265e9fb8d1" :transfer_data {:uuid_account_destination "44444444444"}})

tvalerio16:12:27

but should return true because it’s missing the amount keyword

vemv16:12:45

amount is missing from (s/def :unq/transfer (s/keys :req-un [::uuid_account ::transfer_data]))

tvalerio16:12:06

but amount is inside ::transfer_data how can I validate nested keywords?

👍 1
vemv16:12:36

(s/def :unq/transfer (s/keys :req-un [::uuid_account :unq/transfer_data]))

vemv16:12:01

fixed version, note :unq/transfer_data

tvalerio16:12:40

oh, that’s it. Thanks @U45T93RA6, my tests are passing now :man-facepalming:

🍻 1
leontalbot21:12:55

qq, how do you pass a map as an arg with lein run -m?

leontalbot21:12:22

lein run -m my-ns/my-fun {:opt1? true}

leontalbot21:12:07

obviously this doesn’t work, but I am wondering how to format the hasmap to make my-fun using it

leontalbot21:12:53

such that it calls (my-ns/my-fun {:opt1? true})

vemv21:12:25

fun q! the following turned out to work:

seancorfield22:12:21

Yup, that would turn the sequence of strings into a hash map from string to string, pairwise.

👍 1
seancorfield22:12:04

user=> (let [{:as opts} (range 4)] opts)
{0 1, 2 3}

didibus22:12:49

Don't you just need quotes around it?

seancorfield23:12:39

@U0K064KQV That would pass it in as a string which would then need to be parsed (read as EDN).

seancorfield23:12:40

The suggested solution is to provide the key value data without { } and without : -- just as strings on the command line, and then use & {:as opts} to automatically turn that into a hash map (string -> string).

seancorfield23:12:29

But quoting it on the command-line (as a regular EDN hash map) and then parsing a single argument (first args) from -main [& args] would be another reasonable approach.

seancorfield23:12:20

Either way, the function on the receiving end is going to need to do some work if the goal is to pass values that are not strings (such as true).

👍 1
didibus23:12:23

Ah I see. What's the machinery at play here? Does destructuring auto parses string into maps in any function as well? Or is that specific to the way clojure.main bootstraps the main fn?

didibus23:12:50

Oh I see, the keyword will actually be a string

didibus23:12:11

The given solution just creates a map of string to string

👍 1
seancorfield23:12:52

Right. So either you quote the EDN as-is and the receiving function must parse it (read it back as EDN from a string) or you pass the key/value pairs as plain sequential values on the command line and deal with them as a hash map of string -> string which still may require some parsing.

seancorfield23:12:36

Bottom line, you can't pass actual Clojure data directly into the target function without at least some parsing in a wrapper function) @U07C4S0EM

👍 2
leontalbot23:12:55

ok, thanks guys!