Clojurians
#clojure
<
2018-02-23
>

This page is not created by, affiliated with, or supported by Slack Technologies, Inc.

cddr00:02:07

Am I going insane? After adding this as a dev dependency I'm not seeing it on my classpath. lein with-profile dev classpath |tr ':' '\n' |grep cddr

:profiles
  {:dev {:dependencies [[cddr/with-httpd "1.0.0"]]}}
However if I add it to my project deps, I see it fine.

benzap00:02:11

@cddr you've been stricken by the Gods who overwrite your profiles.clj :dev key, since it doesn't perform a deep merge

benzap00:02:17

had the same issue

benzap00:02:30

I have a fix, hold on

benzap00:02:00

within your project.clj

benzap00:02:38

for some reason, doing it this way performs a deep-merge, if anyone has a better method, i'm all ears :slightly_smiling_face:

benzap00:02:20

note that :dev-profile within the profiles.clj will take precedence, when performing the merge

benzap00:02:27

I'm mistaken! This will still only merge wrt- :dev-common and :dev-profile as though you performed a (merge ...) between them, if I recall it still isn't wholly a deep-merge between profiles

benzap00:02:23

so if you have a :dependencies block in both :dev-common and :dev-profile, you will like only see the dependencies stored in :dev-profile...

benzap00:02:56

If anyone has a better fix, please let me know

noisesmith00:02:58

I assume the {{ in your project.clj example is a typo and should be {?

benzap01:02:52

@noisesmith yes indeed! I'll fix it

caleb.macdonaldblack03:02:12

*with a different key

seancorfield03:02:37

@caleb.macdonaldblack I'm not sure how that can be simplified...

fmind11:02:52

anyone is using the environ library ? I would like to have your opinion on the following PR: https://github.com/weavejester/environ/pull/79

fmind11:02:20

it seems the construction of environ.core/env is reversed, but maybe I'm missing something

qqq11:02:36

besides 'very carefully', is there a way in clojure to handle things that need to be released on gc? for examples: references to open gl array buffers . references to jni c pointers

gnejs11:02:32

uh.. isn’t that backwards, @fmind? You want system environments to override any properties defined in .lein-env don’t you? And you want java props to override environment variables. So that if you do MY_PROP=2 java -Dmy-prop=3 some.jar then (= 3 (:my-prop env))?

fmind11:02:07

@gnejs it's actually the opposite. I want (.lein-env | .boot-env) > system-env > system-props (as the readme state)

fmind11:02:40

but at the moment, my understanding is that the order is reversed (.lein-env | .boot-env) < system-env < system-props

qqq11:02:39

so you want a source file to have higher precedence than user local ENV ?

hawari.rahman1711:02:05

I think what the Readme says by "resolved in the following order" is that it tries to look for values in that order, and replacing any keys that have already exists from previous step. Essentially the system props take higher precedence.

hawari.rahman1711:02:53

So yeah I think it does what it says.

gnejs11:02:54

Maybe it’s a question of how you interpret resolved in the README. But I kinda agree that it can be interpreted the way you suggest. But IMO the order (as it’s implemented) makes most sense.

fmind11:02:02

is "resolved" ambiguous then ? it could mean "find and stop" or "overwrite"

fmind11:02:32

in my situation, I want my dev settings to override the environment variables of my system

fmind11:02:02

I think it makes more sense since the local file (dev) should be isolated from the rest of the system

fmind11:02:58

e.g. if I have a datomic database defined in .bashrc "datomic:<sql://prod>" and "datomic:<mem://dev>" in .lein-env, I want "datomic:<mem://dev>" > "datomic:<sql://prod>" in my project

fmind11:02:36

since .lein-env does not exist in production, only the environment system will be resolved

mpenet11:02:24

it's quite easy to write this yourself, or build it on environ, or use something like juxt/aero to do it

mpenet11:02:42

I doubt the pr will be merged, as it would break tons of ppls code

fmind11:02:46

@mpenet sure, my concern was that the current implementation did not reflect the intended usage

mpenet11:02:00

I guess the wording in the readme could be adjusted

fmind11:02:05

but I had many doubts since the project is popular in the community :slightly_smiling_face:

gnejs11:02:34

Yeah - I’d make a PR to change the wording in the README instead :thumbsup:

gnejs11:02:00

(Because I agree that it’s unclear. This is a better example: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html on how to describe order)

fmind11:02:52

still, don't you think it makes more sense to have .lein-env > environment variables ? At the moment, environment variables overrides the project settings

fmind11:02:25

if you think the current implementation makes more sense, I will simply write a small lib

fmind11:02:07

(oh well, I could simply use https://github.com/kennethreitz/autoenv) but I don't like having magical file settings in my projects

gnejs11:02:24

I think the current impl makes more sense. But that’s just me. I usually use component or integrant to create an application defined env that uses environ but extends it with config from files or default-values as needed.

fmind11:02:48

autoenv + environ provides the behavior that I want anyway

fmind11:02:31

@gnejs I had the same setup, but I like having everything controlled by environment variable. The application is less complex and more explicit.

gnejs11:02:51

Maybe https://github.com/grammarly/omniconf could be interesting? No previous experience of it myself though.

fmind12:02:44

do you think I could set system properties for dev/test with leiningen and use environment variables in prod ?

gnejs12:02:37

of course it’s possible. The downside is that environ builds its config map once during initialization and after that it’s immutable. And there’s no way to create a “new instance” of the env. So if you need to run tests with different environ-params you could be out of luck maybe.

patrkris12:02:55

Guys, what are your feelings about using monads in Clojure? It seems to me that it is used very rarely, even though I can see the value in them

fmind12:02:39

@patrkris just my 2 cents: I think they are wonderful design patterns oriented around composition and abstraction, but they also had another "layer" around plain values

fmind12:02:59

I think Rich Hickey explains it better than me in https://www.youtube.com/watch?v=2V1FtfBDsLU

patrkris12:02:22

Thanks. I never got to see that.

sundarj12:02:14

@patrkris i'm reading the original monad paper by Wadler (it's surprisingly approachable), and he puts it like this: >>>Pure functional languages have this advantage: all flow of data is made explicit. And this disadvantage: sometimes it is painfully explicit. ... It is with regard to modularity that explicit data flow becomes both a blessing and a curse. On the one hand, it is the ultimate in modularity. All data in and all data out are rendered manifest and accessible, providing a maximum of flexibility. On the other hand, it is the nadir of modularity. The essence of an algorithm can become buried under the plumbing required to carry data from its point of creation to its point of use.

leonoel12:02:06

@patrkris what value do you see in the monad abstraction ?

mccraigmccraig13:02:17

@patrkris @leonoel i've used monads a lot in clojure for async programming. once you get used to them the composability and simplicity / comprehensibility of the code really shine

fmind13:02:26

I thing the big problem with monad is that they are tied to the type/value and not the function. You need to wrap-up your data in a context

fmind13:02:47

while in clojure, I think it would make more sense to discriminate based on the type at runtime (e.g. throwable or not)

fmind13:02:02

moreover, you need to agree on the monad implementation. You cannot have two Maybe monad in your langage, although their semantic are the same

fmind13:02:29

if a library owner decides to let monad at the border of its API, it has to be used by the caller as well

leonoel13:02:56

@mccraigmccraig composability and simplicity is not provided by the monad abstraction per se. I don't need to know that {:return list :bind mapcat} is a monad to compose sequence the way I want. Same for async, I can have composable async primitives, with operators fulfilling the monad laws, but what do I yield of the fact that my async primitive share some common properties with sequences ? The only thing that comes to mind is to have a let-style notation, but in clojure this problem is easily solved with macros. In Haskell you need to have that built in the language.

mccraigmccraig13:02:06

not always so @fmind - most of the monad stuff i use works with promises - they are just regular manifold deferreds or bluebird promises

mccraigmccraig13:02:31

yes @leonoel let style notation is a big win - but the macros required are not trivial, so it's easier, and more robust, to have a generic monadic-let working with monadic structures, rather than a bunch of different let implementations for different structures

mccraigmccraig13:02:46

and when you want to reach for applicative-do notation... you definitely don't want to write that too often

leonoel13:02:10

what is applicative-do ?

mccraigmccraig13:02:26

it's a let notation for applicatives ( https://ghc.haskell.org/trac/ghc/wiki/ApplicativeDo ) which gets around the stepwise constraint of monads so you can e.g. do concurrent async operations. it's available in cats as alet http://funcool.github.io/cats/latest/#alet

leonoel14:02:47

thanks, I did not know this notation had a name :slightly_smiling_face:

lodin.johan14:02:08

@mccraigmccraig @leonoel For me the problem is return/`pure`. If you go down this route, you quickly get into trouble.

mccraigmccraig14:02:54

which trouble @lodin.johan? i've got a large codebase mostly using promise monads and haven't encountered any trouble with return

lodin.johan14:02:38

@mccraigmccraig It becomes a problem when you talk about monads in your code, not using a particular monad, i.e. you abstract over monads.

lodin.johan14:02:55

The same problem occurs with Monoid's mempty. What these functions have in common is that the only place the monadic type occurs is in the return value.

lodin.johan14:02:59

pure has type a -> m a (sorry for the Haskell notation). mempty has just t a.

lodin.johan14:02:15

What do you do when you do not have an instance of your applicative/monad or monoid?

mccraigmccraig14:02:27

@lodin.johan you will have to be explicit and provide a type to return... that might be problematic sometimes, although it's not hurt me yet

lodin.johan14:02:38

For me I hit a hard wall when I went in this direction. There were functions that just hurt to much. I introduced a whole new level of debugging if I did the types wrong.

lodin.johan14:02:01

So one particularly annoying, but simple, example is the sequence function.

lodin.johan14:02:03

It was type (Traversable t, Applicative f) => t (f a) -> f (t a).

lodin.johan14:02:51

So an example of that would be to turn [Just 1, Just 2] into Just [1,2].

lodin.johan14:02:47

But if you have any Nothing in there, you get Nothing. [Just 1, Nothing, Just 3] => Nothing.

lodin.johan14:02:01

Now, what does sequence [] give?

mccraigmccraig14:02:54

presumably Just []

lodin.johan14:02:40

Right, but you need to carry the type. And then, if you want to use that result generically, you need to know that you now have a f (t a) instead of a t (f a).

lodin.johan14:02:37

When I tried to go all in on this, it was not a great experience.

mccraigmccraig14:02:14

yeah, that does sound like too much hard work without static checking

lodin.johan14:02:14

You also have things like Monoid a => Monoid (Maybe a), so Maybe a is a monoid if you have a monoid in it. This you want to be able to express.

lodin.johan14:02:33

I think it's doable, but practical ...?

lodin.johan14:02:32

Another thing I find jarring is (<*>) or ap. Applicative functors work really well i Haskell-likes because functions are curried, i.e. it's a -> b -> c instead of (a, b) -> c. And I Clojure in don't want to give up variable arity. Like, how should (ap +) work? You need to either use introspection to see the number of arguments (if single arity) or be explicit about that too.

lodin.johan14:02:18

At this point, I'd just switch to Haskell/PureScript if I wanted to program in that style. :slightly_smiling_face:

mccraigmccraig14:02:29

yeah, i agree with that - otoh sugar around a promise monad has made async programming very nice

lodin.johan14:02:30

Yeah, and Clojure has sugar (arguably better than do-notation) for lists, i.e. for.

lodin.johan14:02:37

But talking about monads in general as its own thing in Clojure (or any language where a type system does not do inference on return values) will be awkward, I think.

lodin.johan14:02:47

Maybe not necessarily inference. I don't know how Scala people do it; they manage it through the type system somehow. Whether it's awkward or not, I don't know. :slightly_smiling_face:

mbjarland14:02:11

is there a construct in clojure for if x fulfills predicate, return x, otherwise return y, I'm aware I can do this with let and if:

(let [r (some-calculation)]
  (if (pred? r) r (other-calculation)))
but was hoping for something terser along the lines of (ifp pred? (some-calculation) (other-calculation))...I also suspect I already received an answer to this once, but since the channel history is what it is and I'm apparently getting senile I'll post it again

sundarj15:02:03

@mbjarland i believe the answer last time was this macro being written: https://gist.github.com/madstap/52f280f17289f1a1ec81f3c9f0b23778

thedavidmeister16:02:12

@mbjarland if you write your pred to return the value instead of a boolean then (or (pred? (some-calculation)) (other-calculation)) should be enough right?

reborg17:02:07

Something close @mbjarland (let [r (some-calc)] (cond-> r (not (pred? r)) other-calc))

mbjarland17:02:45

@sundarj@thedavidmeister @reborg thanks all...somehow this still leaves me strangely unsatisfied but perhaps that just means it's time to write a custom macro. The or solution is nice, but then you can not use most of the out of the box predicates as they tend to return true/false and most of the other solutions I've come up with still need a let binding in some way

noisesmith17:02:16

Or a helper function: (defn and-pred [x p?] (and (p? x) x)))

mbjarland22:02:03

@noisesmith wow, and...totally missed that one. Nice!

noisesmith22:02:46

so it's usage might look like (or (and-pred 10 even?) 2)