Fork me on GitHub
#beginners
<
2017-05-01
>
eslachance00:05:59

So, me and a few people want to start a new project that is essentially a web-based feedback system (as a uservoice replacement). For the purpose of our project we want to use a full clj/cljs stack, mostly for performance reasons but also because we know Clojure excels in fully integrating a front-end and back-end system. At least more than any other language I've seen. So my question is, what would be a basic stack that you guys would recommend? I know this is highly subjective, but ideally I'd be looking for something that I could start off a project with. A lein template, perhaps? There's so much stuff out there... re-frame, reagent, om next, etc etc... I'm a little lost.

donyorm01:05:14

@eslachance If you are just starting out luminus (http://www.luminusweb.net/) is pretty nice

jumar08:05:44

Yeah, luminus has many different profiles depending on what you want to use - see http://www.luminusweb.net/docs/profiles.md As regards to frontend I'd probably recommend to go with reagent since it's simpler (subjectively) and I've seen multiple projects migrating from Om to reagent/re-frame.

curlyfry09:05:32

+1 for re-frame in the frontend. The docs for both Luminus and re-frame are pretty great, so that might help a bit. In fact, I think the Luminus docs are worth reading even if you decide not to use it, it can give you a lot of guidance and inspiration on how to build a Clojure webapp.

matan11:05:03

I think the answer would be no: Can I call one prototype of my function from another, or make a recursive call, without actually using the function's name? like a this-ness to a function, so that if I later rename the function, I do not need to touch its body? or bodies in case of a multi-prototype function

matan11:05:02

@eslachance as much as it matters, I plan to use om.next for a similar scenario, which I figure more radically integrates back-end and front-end under a single paradigm. om.next rides react.js. But this may run into two challenges: (1) it would feel different than how you do web development in other languages (2) maturity of the docs and general maturity might not be at par yet.

matan11:05:06

maybe re-frame is more mature

curlyfry11:05:42

@matan Not sure if I understand that question, but a function is a valid target for recur

eslachance13:05:17

Thank you @donyorm and @matan for the responses!

petr.mensik13:05:53

Hello guys, just a quick question, does anybody know why this code doesn't catch an exception when it's called with string as an argument? It just seems right to me, so what could be the problem?

(defn parse-int-or-default
  "Tries to parse Integer or return default value"
  [s default-value]
  (try
    (Integer/parseInt s)
    (catch NumberFormatException e default-value)))
I am calling it with (parse-init-or-default "aaa" "noo") so I would expect nooo to be returned. However I just get the excepion back. Thanks a lot

byron-woodfork14:05:17

@petr.mensik I believe it's because you're attempting to catch the wrong exception.

byron-woodfork14:05:37

user=> (Integer/parserInt "sup")

CompilerException java.lang.IllegalArgumentException: No matching method: parserInt, compiling:(/private/var/folders/l_/7g0qhbzn55lgr2qbmtnv0dw00000gn/T/form-init6553036324918927450.clj:1:1)

Seems to throw an IllegalArgumentException

petr.mensik14:05:40

@byron-woodfork I also tried with plain Exception

petr.mensik14:05:33

However for me it's giving NFE

petr.mensik14:05:09

Yeah, that's why I am asking 😄

petr.mensik14:05:10

However here it works as expected https://goo.gl/AnsvuJ

petr.mensik14:05:58

omg, got it,, I forgot I had function with the same in namespace so it was calling that one... my bad, sorry guys 😕

byron-woodfork14:05:29

I was definitely scratching my head wondering

matan16:05:15

@curlyfry I mean, must I use get-scenario-executions in the below, to refer from the second definition to the first one?

matan16:05:11

Some kind of this-ness could have been nice, instead, I think.

matan16:05:29

I think there is no idiomatic way for that (no concept of this for a function), other than delving your own macro writing

matan16:05:26

My question is general, not specific to this one snippet

noisesmith16:05:39

@matan the standard way to do it is to use the function’s own name, this even works with anonymous functions (fn foo [...] ...)

matan17:05:54

@noisesmith thanks, just checking

matan17:05:45

then if I use def fn instead of def I can sort of accomplish some "this-ness" bliss, because then I can give the function a different name than the var that points to it. Nice drill, thanks.

matan19:05:43

Multimethods. I am looking to dispatch by argument types, to go beyond the arity-based dispatch of functions having multiple argument-list definitions. Would that be a very good use case for a multimethod?

matan19:05:51

Problem: I don't see a very elegant way of defining my own data types to use in conjunction with a defmulti and its defmethods

mtkp19:05:14

maybe look at defprotocol and defrecord

matan19:05:41

Indeed the docs only show java classes as data types used with multi-methods https://clojure.org/reference/multimethods

matan19:05:52

Or they stick a keyword as a "type" signifier, into maps

matan19:05:23

Which I'd call a bit lame, in both cases, compared to the promise of > Clojure supports sophisticated runtime polymorphism through a multimethod system that supports dispatching on types, values ...

matan19:05:49

So what am I missing?

donaldball19:05:59

The multimethod dispatch fn is a fn of all of its args. It’s quite common for it to only use the type of its first arg, but I’ve certainly written more complex dispatch fns and only felt a little bad about it.

matan19:05:26

@donaldball Sure, however I wonder if I can give maps (and other collections) a type for the sake multimethod disptach, without the ceremony of stuffing a keyword inside them which the docs show, otherwise dispatching on primitive types is way too limited

donaldball19:05:20

An example might clarify. It is not uncommon for complex dispatch fns to essentially classify maps into a small number of buckets based on their contents.

matan19:05:50

Yes, that I can do. Then the typing is implicit in the code of the dispatch function, but lives not outside of it for other multimethods to enjoy.

matan19:05:09

So then in my example, I can check if a map has a certain structure (e.g.`{:keys [a b]}`) in a dispatch fn, but this will not make this "type" known for other multimethods.

matan19:05:31

Oh! unless this dispatch function is shared by many multimethods!

matan19:05:03

Rather than defining a dispatch function inline inside the defmulti!

matan19:05:25

I think I now got it!

matan20:05:19

Not that anyone would care, but I implemented a multimethod for dispatching based on argument type, and frankly, it looks worse than just giving each method a name implying its argument type. I see no added value, because there's no validation api built-in in the language. Until clojure has built-in validation api (waiting on clojure 1.9) data types are just too lame for me. @mtkp defrecord builds objects that flatly hold fields, they do not really attach a type to maps but thanks anyway.

noisesmith20:05:23

> defrecord builds objects that flatly hold fields, they do not really attach a type to maps defrecord defines a new class that is mostly like a hash-map

noisesmith20:05:59

how is that different from attaching a type to a map?

matan20:05:51

Yep, which is not very conducive to attaching types or validating the structures of maps, or nested maps. @noisesmith the difference is (I think) that values in objects created with defrecord need to be accessed with the dot . operator, rather than like ordinary clojure data.

noisesmith20:05:00

that’s false

matan20:05:01

So there's a choice between plain useful clojure data, without typing or built-in validation api, v.s. defrecord which relegates to object api for any access of the actual data.

noisesmith20:05:12

it’s possible to access them with ., but never neccessary

matan20:05:13

@noisesmith maybe I've looked at wrong example code then

noisesmith20:05:27

@matan likely the example code was doing something dumb.

noisesmith20:05:36

@matan

justin@S: ~$ rlwrap java -jar ~/bin/clojure-1.9.0-alpha15.jar
Clojure 1.9.0-alpha15
(ins)user=> (defrecord Foo [bar baz])
user.Foo
(ins)user=> (:bar (->Foo 1 2))
1
(ins)user=> (keys (->Foo 1 2))
(:bar :baz)
user=>

noisesmith20:05:02

(ins)user=> (type (->Foo 1 2))
user.Foo

donaldball20:05:05

Yeah, I should have mentioned when you came to your realization about dispatch fns: generally when it looks like I have more than one multimethod that shares a dispatch fn, I start looking hard at finding a protocol instead.

matan20:05:58

@noisesmith usually I do not use the threading macro to access map keys, maybe my clojure is just primitive ..

noisesmith20:05:09

@matan that is not the threading macro

noisesmith20:05:28

it’s a constructor function called ->Foo created by defrecord

matan20:05:54

Well it is very different than handling plain clojure collections then ..

noisesmith20:05:08

all the plain clojure collections have constructors

noisesmith20:05:40

user=> (= #user.Foo{:bar 1 :baz 2} (->Foo 1 2))
true

matan20:05:55

@noisesmith Yes well defrecord forces me into using collections in more sophisticated ways than I typically do, in my primitive evolutionary stage 🙂 maybe I'll try to make some advances.

noisesmith20:05:55

it’s just an alternative to using the reader literal

matan20:05:33

@donaldball yes I understand. that's why I was looking at defrecord following the prior notes.

matan20:05:45

Frankly I currently feel more natural using plain data, not record objects nor protocols. With a good validations library, I'd still use a dispatch function for assigning some types and validations to some recurring map structures. I think.

matan20:05:47

I feel like doing Java, with records and protocols 🙂 more so with records (`defrecord`) and deftype. silly me.

noisesmith20:05:15

maybe I don’t understand what you mean by “type” - defrecord is the simplest way to make a new class in clojure that is usable with idiomatic clojure code

matan20:05:29

@noisesmith right. which I'll avoid for a while, because it feels like a backdoor OO added to clojure, or something along that line.

donaldball20:05:37

Also worth noting then that I almost never reach for protocols unless the behavior in question has side effects 🙂

matan20:05:03

donaldball: just wondering whether you happen to typically use a certain validations library

donaldball20:05:31

I’ve been using spec in production for ha going on a year now looks like

noisesmith20:05:01

defrecords are not backdoor OO - they are regular old OO in an OO language, skipping the parts we don’t like (mutation, inheritance, information hiding) and creating data that is 100% compatible with the functions defined in clojure.core

mobileink20:05:40

@matan i guess i don't see what the problem is. defmulti allows to to dispatch in whatever way you please - the dispatch fn can use anything to decide how to dispatch - including global even mutable data. if you wan some kind of global type-detector, just write a fn for it and use it in your dispatch fn. validation is a whole 'nother thing. what am i missing?

matan20:05:11

@noisesmith I'll try to adopt your (probably more correct) perspective and use some records instead of maps, when I wish to have some sort of type guarantees to them. I believe maps are objects too, so not much of a difference in footprint. Thanks for the discussion!

noisesmith20:05:57

matan: not only are hash-maps objects, functions are too - clojure isn’t about abstracting the details of the vm

tbaldridge20:05:17

there are a lot of differences in footprint between records and hashmaps

tbaldridge20:05:45

For example {:foo 1 :bar 3} requires storing 4 "things". (->Record 1 3) only requires storing 2.

tbaldridge20:05:08

That being said, performance is really the only reason to use records, and is the reason they were added to the language.

tbaldridge20:05:39

multimethods and hashmaps are slower than records + protocols. Not enough that you should care in most situations though.

noisesmith21:05:17

one great use of records that I’ve found is to replace a map with a record in order to get better profiling for data created / captured in a specific context

noisesmith21:05:33

since with all the vanilla structures we use, a bunch of useful profiling tools become less helpful

matan21:05:12

@noisesmith how in general would you use that for profiling?

noisesmith21:05:00

@matan my profiler shows me an object heirarchy for things in memory - when I have a specific type I can look for, I can distinguish it (and the data it captures) from the gazillion other hash-map instances in the running program

matan21:05:13

ah right, of course

noisesmith21:05:32

otherwise it’s like “10000 hash maps exist” and I get to have fun guessing which one is which by clicking into their keys

matan21:05:22

@noisesmith lol, or rather 😢

noisesmith21:05:05

I’m actually profiling my clj app right now, but it’s more of a cpu profile in this case

matan21:05:41

I wish these subtle differences between these alternatives had been clearer in the main docs outlets.

matan21:05:21

I'd be coding 2 years without learning half what was mentioned on this discussion. I mean who would have thought records are equivalent to maps and even more performant.

noisesmith21:05:44

well, (doc defrecord) will tell you that right in the repl 😄

matan21:05:06

you probably also read man pages 😜

matan21:05:42

or more seriously, if the online docs had breaks between their paragraphs, I'd probably have read it there. so from now on, doc in the repl it is 🙂

noisesmith21:05:17

yes, I also read man pages, and I read the encyclopedia and the dictionary, and I’d rather read the rules for a table top game than play one 🤓

matan21:05:20

well, when faced with a documentation blob without any formatting or line breaks, I fail to skim-read and might quit too early. https://clojuredocs.org/clojure.core/deftype 🙁

noisesmith21:05:12

oh wow, someone needs to work on their CSS I think

mobileink20:05:12

if you want to validate an arg to a multifn, i believe you can do that in the dispatcher.

matan20:05:48

@mobileink yes, and types and validations, as concepts, are much related because a type typically requires a structure, whereas validations assert a structure. two very similar things. when not using hardwired types, I'd validate structure in my dispatching function.

mobileink21:05:44

yeah, in a type language, type = validator (at compike time) but we don't have that. sorry to be dense but i don't see what problem you're trying to solve.

matan21:05:25

the problem of validating structures without repeating myself, while understanding how the different alternatives compare. more or less solved now.

matan21:05:16

@noisesmith @donaldball @tbaldridge @mobileink thanks for the great discussion. really much appreciated!

matan21:05:12

I guess this was just a single cake discussion for some of us

mobileink21:05:32

if you're feeling a little nutty you can take a look at some experimenting i did a while back exploring various polymorphisms supported by clojure, including one not used (afaik) - multimodeling. https://github.com/mobileink/lab.clj.polymorphism

matan21:05:03

mobileink: Cool stuff! I should look

noisesmith21:05:07

@mobileink in the readme you mention that defprotocol has side effects of creating vars and mutating the namespace mapping. What about the other side effect of creating an Interface?

mobileink21:05:13

i think some of the doc might be useful. looking at the code much later, erm, cough, something in there works. if you grok algebra, this should be fairly clear, even if the impl. is opaque: https://github.com/mobileink/lab.clj.polymorphism/blob/master/src/clojure/multimodels/examples.clj

matan21:05:06

@mobileink you've really dug deep into the rabit's hole 🐇

mobileink21:05:34

i swallowed the red pill. or was it the green one? i forget. ;)

mobileink21:05:47

@noisesmith you mean definterface?

noisesmith21:05:56

no I mean defprotocol

noisesmith21:05:01

it creates an interface

mobileink21:05:29

ah, good point. doc prs welcome! ;)

noisesmith21:05:02

heh, now that I look around it, it’s right before it does the var alteration…

mobileink21:05:55

seriously, help wanted. the original goal was minimal but complete exploration of the varieties of polymorphism in clj, with full docs, understandible by noobs.

mobileink21:05:58

not only that, but clear illustration of the fundamental algebraicity of clojure.

mobileink21:05:02

personally i think it's pretty cool that we can do what i called multimodelling in clj - switch the structure (implementation) of a signature (interface, protocol) at runtime. real algebra!

mobileink21:05:57

@noisesmith wait! i did mention interface! "Yet another side-effect: defprotocol generates a Java Interface..." woot!

noisesmith21:05:16

oh, clearly I missed it

mobileink21:05:13

np, thanks for even looking!

josh.freckleton22:05:51

does lein uberjar refer to git commits at all? IE if I add something that's not commited, will my change end up in the jar?

carly22:05:54

Your change will end up in the jar.