Fork me on GitHub
#beginners
<
2018-05-31
>
athomasoriginal00:05:55

Hey all, I have sample data like this:

[1 4 7] [2 5 8] [3 6 9]
and I want to turn it into this:
[1 2 3 4 5 6 7 8 9]
My current solution:
(apply concat (apply map vector [[1 4 7] [2 5 8] [3 6 9]]))
What I really want to do is fuse all three vectors into 1, where the first index from each follows the next...for example, here is another data set
[:a 4 7] [:f 5 8] [:k 6 9]

will need to turn into

[:a :f :k 4 5 6 7 8 9]

andy.fingerhut00:05:49

(interleave [1 4 7] [ 2 5 8] [3 6 9]) returns (1 2 3 4 5 6 7 8 9)

andy.fingerhut00:05:23

The version you wrote looks perfectly fine, too, if you prefer to use it.

athomasoriginal00:05:58

yep, thats exactly what I was looking for 🙂 I prefer yours actually as I believe its more idiomatic and I figured there was something like your available, but could not remember what it was called

noisesmith00:05:49

there's also (apply mapcat vector)

noisesmith00:05:32

but (apply interleave) is simpler by one symbol :D

Chester Heng11:05:00

Hi. Anyone can share how to design a full stack web app in clojure and clojurescript?

gklijs11:05:39

Hi, @chester.heng there a lot of options. What kind of web app are you thinking of, a SPA, or something more traditional?

Chester Heng11:05:55

something like this

Chester Heng11:05:24

I am not sure how to run it

gklijs11:05:28

@chester.heng front-end end back-end is in two folders, seems like you first needs to start the back-end, command is in the readme of the simple-sever, then start the front-end, info also in that readme

Chester Heng11:05:49

actually i want to design a full stack SPA with login. i google internet. all list down the skills set required for frontend and backend.

Chester Heng11:05:13

i cannot find any steps to design a full stack SPA with login.

Chester Heng11:05:44

I know the first step is design database schema. After that ???

Chester Heng11:05:54

I aim to use clojure and clojurescript

gklijs11:05:58

Adding a login is not that different, you need some login entrypoint on the server, and may put something in the session, which you can use in calls after that one.

gklijs11:05:41

so something like this on the server

(defn handle-login [uid pass session]
  (log/debug "login with " uid ", old session :" session)
  (if-let [profile (second (repo/get-map :u uid))]
    (if (hashers/check pass (::admin-spec/password profile))
      {:status 303 :session (assoc session :uid uid) :headers {"Location" "/"}}
      {:status 303 :headers {"Location" "/login"}})
    {:status 303 :headers {"Location" "/login"}}))
then afterwards you can get the user from the session on the server. I have a project where I validate opening a websocket for a logged in user, so all the communication between the server and the side can go to the websocket. But there are many other ways.

Chester Heng11:05:50

@gklijs Thanks. I am beginner in clojure. Hoping to develop a full stack clojure web app.

gklijs11:05:24

I would start without the login, it’s easy to add later.

Chester Heng11:05:48

@gklijs Thank. I give it a try.

soulflyer13:05:39

@chester.heng It might be worth taking a look at luminus for the back end. I seem to remember that covers basic user login quite well. If you are planing on using reagent in the front end, you could also take a look at re-com and re-frame. Both are great libraries with some very cool features.

funkrider13:05:33

Hi all - I was wondering what folks are using for CI / CD platforms when using Clojure? Have people had success using one tool or have folks had problems?

rnagpal13:05:02

This creates a simple route handler as well

rnagpal13:05:10

and glues up everything for you

bj15:05:53

I've set up a multimethod using defmulti, and a default implementation in (defmethod methodname :default ...). When I create another implementation, say (defmethod methodname :custom ...), is there an idiomatic way to call the default implementation from within the new one?

noisesmith16:05:48

you can retrieve the default method

user=> (apropos "multi")
(clojure.core/defmulti clojure.core/unchecked-multiply clojure.core/unchecked-multiply-int clojure.spec.alpha/multi-spec clojure.spec.alpha/multi-spec-impl)
user=> (apropos "method")
(clojure.core/-reset-methods clojure.core/defmethod clojure.core/find-protocol-method clojure.core/get-method clojure.core/method-sig clojure.core/methods clojure.core/prefer-method clojure.core/print-method clojure.core/remove-all-methods clojure.core/remove-method complete.core/ns-java-methods)
user=> (defmulti foo identity)
nil
user=> (defmethod foo :default [_] "default")
#object[clojure.lang.MultiFn 0xb020a6 "clojure.lang.MultiFn@b020a6"]
user=> (defmethod foo :custom [x] (str "not-" ((get-method foo :default) x)))
#object[clojure.lang.MultiFn 0xb020a6 "clojure.lang.MultiFn@b020a6"]
user=> (foo :a)
"default"
user=> (foo :custom)
"not-default"

bj17:05:56

Thank you! I didn't realize get-method existed

noisesmith17:05:44

of course it's better to bind the result of get method instead of using the "double banana" call

mg15:05:43

@bj multimethods are really intended for dynamic dispatch. You’re describing a situation where the caller already knows exactly what it wants to call, so maybe it would be better to just use a function for that

noisesmith16:05:49

not necessarily, it's valid to write a method that uses a call to another method of the same multi internally, and it's possible to look up method functions by dispatch value

athomasoriginal15:05:06

@ian.davies I have used circleci and jenkins. They both work well. What kind of issues were you thinking you might run into with CI/CD tools?

jgh15:05:32

circleci is nice, has a good integration with github, and an interface that doesnt make your eyes bleed.

jgh15:05:42

(plus they use clojure)

mg15:05:10

and the creator of leiningen works there

ScArcher17:05:31

What's the best practice for specifying specs in :pre / :post for fucntions? Should this be specified in subsequent functions, or just at the "highest level" function in your system? Is the spec validation expensive (just looking at keys and types for now)?

noisesmith17:05:48

:pre and :post can be turned off globally, but generally "boundary" places are better for that kind of assertion - places where outside code integrates

ScArcher17:05:33

Perfect, thanks!

seancorfield18:05:01

@scott.archer It's also important to consider how you want a spec validation failure to be handled. Is it something you want to deal with inside a function (for example, validating/conforming data originating from users or external programs) or is it something where you want to prevent calls completely with invalid data?

seancorfield18:05:27

Also, as @noisesmith indicated, the assertion-like things in Clojure (`assert`, :pre, :post, etc) can be turned on/off so you have to consider whether you want them to be dev-only or dev & prod. And whether you're happy to live with AssertionError failures rather than "normal" exceptions...

noisesmith18:05:52

it would be nice to have a variant on pre/post/assert for IllegalArgumentException (runtime prod situations) rather than AssertionError (catching dev mistakes)

ScArcher18:05:08

I guess for development i'd want to leverage :pre / :post to make it easier to spot issues.

noisesmith18:05:29

the rationale for AssertionError IIRC being that you shouldn't catch error subclasses, they should fail and you should fix the code

ScArcher18:05:30

In production, would I just add a call to the function to do validation / throw an exception?

noisesmith18:05:13

well, there's functions to get a boolean or descriptive message as to whether a spec was matched, and you can do whatever makes sense with that info

noisesmith18:05:27

(probably not throwing AssertionError for production code in that case)

ScArcher18:05:02

Right, i posted some code, where if spec/valid? is false, I call (expond) to print a nice error.

seancorfield18:05:08

Our most common pattern is

(let [data (s/conform ::some-spec input-data)]
  (if (s/invalid? data)
    (handle-bad-data ::some-spec input-data)
    (do-good-stuff data)))

ScArcher18:05:52

(defn valid? [spec-arg data-arg] (if-not (spec/valid? spec-arg data-arg) (expound/expound spec-arg data-arg) true))

ScArcher18:05:09

That's what I have for now.

ScArcher18:05:43

I'll see how I can improve it now that I understand the :pre / :post stuff.

seancorfield18:05:22

That's logically the same as (or (spec/valid? spec-arg data-arg) (expound/expound spec-arg data-arg)) @scott.archer

ScArcher18:05:24

So in general :pre / :post is good for development / testing, but it shouldn't be relied on for user data validation as it can be disabled in production.

seancorfield18:05:07

(if-not cond expr true) => (if cond true expr) => (or cond expr)

ScArcher18:05:53

Understood, i'm still trying to move from non-functional style to functional, so I'll probably do that a lot until I start to recognize those patterns.

ScArcher18:05:59

No need for an if

seancorfield18:05:02

Re: assertions (including :pre/`:post`), I'm a strong advocate of leaving them enabled in production. If they're meant to indicate "this shouldn't happen", what should the program do if that condition is violated in production? Continue with bad data, or fail fast?

seancorfield18:05:29

In general, if you see a conditional that has true or false in one of the branches, it's a bit of a code smell and it can (and probably should) be rewritten using and/`or` as a Boolean expression.

👍 4
dfcarpenter19:05:28

Sorry if this is a stupid question but i'm struggling to understand exactly what Component and Mount are used for and when it's appropriate to use them.

noisesmith19:05:55

for mutable state that needs to be shared between various places in your code, and needs to be initialized in a specific order

noisesmith19:05:44

they do a tree-sort to make sure dependent things are initialized in dependency reverse order

noisesmith19:05:13

and provide a simple handle to start up / shut down your whole app repeatedly without needing to exit a vm

seancorfield19:05:19

@dfcarpenter For example, when a program interacts with a database and needs to create a connection pool at startup and tear it down on shutdown.

dfcarpenter19:05:35

So an abstraction to sanely handle singletons for different services/mutable objects?

seancorfield19:05:08

Your Application Component might depend on a Configuration Component and a Database Component. The Database Component might in turn depend on the Configuration Component too. You declare that dependency to the Component library and then you create the Application and start it, and the library will create Configuration and start that, the create Database and pass in the Configuration and start the Database, and then finally pass both into Application and actually start that. Essentially.

seancorfield19:05:24

So, sane lifecycle and dependency management for "services".

seancorfield19:05:37

The nice thing is that your -main function then becomes something like

(let [system (component/start (app/build-system args))]
  (-> system :completed deref))
(my Application Component typically has a promise that is delivered in the stop function so -main can block on that -- which allows a program to be programmatically shutdown)

seancorfield20:05:12

The nice thing is that you can do exactly the same thing in the REPL -- calling start and stop on your Application Component -- so you can work without exiting the REPL, starting and stopping your application as needed.

dfcarpenter20:05:27

Yes, that was helpful thank you

noisesmith20:05:06

@dfcarpenter regarding singletons, that's the design difference between component and mount, component avoids making any singletons anywhere, mount makes each state holder a global mutable singleton

noisesmith20:05:04

the advantage with component is that means you can easily generalize each state object, and localize their usage explicitly, the advantage of mount is that you know exactly where each thing is defined and it's simple to access them in client code via their global handle

dfcarpenter20:05:19

@noisesmith Thanks for explaining the differences

noisesmith20:05:53

oh, a place where that difference matters: in a test, "mocking" a component means passing a simple hash map as an argument (replacing some other component), where the equivalent in mount means rebinding a global var from another namespace

noisesmith20:05:20

but I guess that's implicit in using global singletons vs. not using them

dfcarpenter20:05:04

Ahh ok. Right now im stuck deciding between deps, lein, and boot and then mount / component.

noisesmith20:05:19

well, deps isn't a build tool. I'd say mount/lein are on the "friendly and sometimes messy" side, and boot/component are on the "correct but more ramp-up" side, if that helps the decision

noisesmith20:05:51

there's things component and boot require you to do that are just automatic with mount and leiningen

hiredman20:05:37

I would object to that

hiredman20:05:22

or I do object to that

hiredman21:05:28

characterizing boot as more correct than lein is silly, given how boot sort of abandons any kind of principle stand letting you do whatever, and people do terrible things with it

hiredman21:05:42

(component is clearly correct over mount though)

dfcarpenter21:05:19

Could someone point me to a Component setup in the wild that is not to complex so I can understand how to set it up?

hiredman21:05:37

have you read the readme?

dfcarpenter21:05:48

I did, helped quite a bit but still would like to see more examples

hiredman21:05:18

I don't know of a good example project or something like that, I have https://github.com/hiredman/songs-of-future-past/blob/master/src/com/manigfeald/sofp.clj which is the only open source place I've used it I think. component is really simple, most people over think, or think it is doing more than it is when they first see it

queerbiohackinghuman21:05:59

How would I make the CLJS compiler emit a literal JS translation for a given file to be consumed by another build process?

noisesmith21:05:30

for literally making the cljs compiler do that, the official quickstart shows how

noisesmith21:05:55

for doing that from one of the build tool plugins like lein-cljsbuild, I'm not even sure if it's possible

noisesmith21:05:46

ugh, they changed the quick start and it no longer shows how to do this, never mind, you probably don't want to mess around with the cljs api directly anyway

queerbiohackinghuman22:05:23

Thanks for your response! Are the implications of what you've said that I'm Shit Out Of Luck?

noisesmith22:05:12

not necessarily - the clojurescript compiler api can generate a string for you, the tricky part is making that part of your build tooling, I'd check with the documentation for the build plugin you are using - boot is much easier to extend to do what you want, but lein is easier in general

hiredman21:05:12

https://gist.github.com/hiredman/075b45eaeb01e4b526ce6f8854685487#file-ur-component-clj-L1-L10 is a thing I wrote to try and demonstrate how simple component is, this is kind of a stripped down version of the core of it

hiredman21:05:41

there is also a #component channel

seancorfield22:05:05

@dfcarpenter I'll chime in on the deps vs lein vs boot question: my recommendation to everyone just getting started with Clojure now is to use clj and deps.edn if you can, since that's a "core" part of Clojure now, officially documented on http://clojure.org and supported by the Clojure/core aka Cognitect folks.

seancorfield22:05:38

That said, nearly all of the literature and tutorials out there use lein -- pretty much by default since it's been around the longest and it's "simple": you write project.clj and then lein run runs your program, lein repl starts a REPL with your program, lein test runs the tests, lein uberjar produces a JAR that can be deployed and run with java -jar ....

seancorfield22:05:34

However, lein isn't always very intuitive when things go wrong or you wander off the beaten path. boot is a lot less "magical" and it uses a build file that is "just Clojure" so it can be a lot easier to customize and debug. But, yeah, boot requires more upfront knowledge than lein.

seancorfield22:05:52

(I use "simple" in quotes for lein since, really, it's easy more than it is simple in the Clojure sense)