Fork me on GitHub
#clojure
<
2018-05-23
>
sophiago00:05:53

I know this is discussed rather frequently here, but I have questions about currying... I was able to find a whole variety of currying functions from the mailing list years ago that are compiled here: https://gist.github.com/rlm/746185

sophiago00:05:43

The "decorated" macro doesn't fit my use case, so I'm really just using curry* from the above example, which means I can't simply expand it for debugging purposes

sophiago00:05:54

My use case is applying this (kind of nasty) curried function to convert Church numerals into strings, primarily in order to test for equivalency: (fn [f] ((f (fn [n] (format "f(%s)" n))) "n"))

sophiago00:05:23

Very often I want to apply it to a binary Church numeral, e.g. (def one #(%1 %2)), or to ones that simply move the inner binding site, e.g. (def one (fn [f] (f (fn [x] x)))) vs. the canonical (def one (fn [f] (fn [x] (f x)))). I think both of these should be possible by currying the second function, but I'm not having any luck using the techniques from this gist 😕

sophiago00:05:10

For the non-unary case, I went so far as writing a macro transform functions that look like anonymous literals into gensymed univariate fns with the bindings all stacked at the top level.

sophiago00:05:06

The second example is even weirder...using a fork of the compiler that allows nested anonymous function literals I had to do (def one #(let [x %] #(x %))) rather than simply (def one #(% #(%))) just to deal with the mismatch of binding sites.

sophiago00:05:59

I keep thinking it may actually be as simple as writing a more clever printer than (fn [f] ((f (fn [n] (format "f(%s)" n))) "n")) rather than trying to transform the functions I'm applying it to. The structure means that requires something beyond simply uncurrying, i.e. calling apply, though.

ghadi00:05:36

Somehow I missed the question

ghadi00:05:45

Can you restate?

sophiago00:05:18

Put more tersely: I would like to uncurry a function so it can be applied without respect for binding sites (only order). Example: ((fn [f] ((f (fn [n] (format "f(%s)" n))) "n")) #(%1 %2))

sophiago00:05:21

A different example since the callee is also curried is: ((fn [f] ((f (fn [n] (format "f(%s)" n))) (fn [f] (f (fn [x] x))))

sophiago01:05:01

Put differently, say I have (def cprint (fn [f] ((f (fn [n] (format "f(%s)" n))) "n"))) and I want the same behavior except with arguments more like (defn cprint [[f n]] ((f (format "f(%s)" n)) "n"))

sophiago01:05:47

Or whatever would allow me to call (cprint (fn [f x] (f x))) as well as (cprint (fn [f] (f (fn [x] x)))) rather than only (cprint (fn [f] (fn [x] (f x))))

sophiago01:05:54

Hope that makes sense

justinlee02:05:38

this is giving me flashbacks to grad school

😄 4
andy.fingerhut02:05:45

Its lambdas all the way down ...

sophiago02:05:53

In some ways, Clojure is a step forward because it's not lambdas all the way down. On the other hand, I find it often makes standard lambda calculus tricks more difficult. I'm sure people are thinking, "Well, those are never things you'd use in production," but a lot of this speaks to things like argument order that I know Rich put a lot of thought to and maybe will continue to since he seems unsatisfied as of yet (pretty sure he mentioned this in last year's Conj keynote). This example demonstrates how currying actually doesn't solve that problem like many people assume.

sophiago02:05:08

I think currying solves it in terms of the very specific use case of typechecking. But when it comes to source transformation things get much more complicated. I often want to write macros that function like apply-template (not sure what the story is with clojure.template...I've played with it a bunch recently, but have never seen it used anywhere) except that reduce after splicing. Of course Clojure can't do that because many reductions are nondeterministic.

sophiago02:05:57

But I think it's not unreasonable to say, "Well, I want two binary functions to compose in a manner where the variables are substituted in the order of their binding and then I'm going to call eval on certain parts of the resulting AST that I know actually can be reduced deterministically."

sophiago02:05:40

If you look at these examples, that's essentially what's going on here. The nesting just makes it impossible to use apply-template and even if I could it would be semantically equivalent but not syntactically. That's actually the whole need for the function that calls format in the context of Church encoding! The easiest way to check equivalence, i.e. reduce lambdas to forms that are syntactically equivalent, is through contrived string generation.

seancorfield04:05:18

@sophiago I'm curious what domain you're using Clojure for and what sort of production applications you're building?

sophiago04:05:20

Nothing at the moment actually. I've used Clojure as the primary language for my personal projects for some years now, but most recently worked in Haskell and it seems my next job will probably be in either Haskell or OCaml (just based on demand and what I'm interested in at this point). Anyway, everything I'm talking about above just stemmed from a language-agnostic idea I had a while back regarding lambda binders and working memory while programming and am only now finalizing as a blog post. I wanted to actually quantify how variables are tracked in these different styles using tools.analyzer, which is something I could actually use for source transformation in my automatic differentiation library, but am putting that part off so I..you know...actually finish a blog post.

sophiago04:05:42

I remember we talked about this currying issue before. I really am interested to see if Rich ends up going anywhere as far as how we interact with function arguments, even though I haven't agreed with many of his more recent opinions.

sophiago04:05:57

I suppose the main library I'm working on is intended to be a production application, even though I'm not being paid for it in the form I'm developing it in Clojure. It started from thinking about speeding up multivariate automatic differentiation, but has moved more towards fast integer arithmetic and vector calculus functions that operate on an IR form I can transform regular Clojure code too and from (although that last part isn't out yet).

sophiago05:05:24

So the idea is to add some macros that use tools.analyzer so you'd be working on a higher-level than this ugly format with tuple-keyed sorted-maps. I also tried int-maps for the individual polynomials, but eventually you end up needing to develop custom integer representations to have the same functionality. I'm not sure I'll get to that point in Clojure because various issues with Java, both mine and the environment's.

seancorfield05:05:19

Wow... I haven't done PDEs since... 1980 I think...

seancorfield05:05:09

That was first year analysis at university. I vaguely remember quite enjoying that course...

sophiago05:05:40

Ha. Well, there are many possible applications of this. I might actually get to do stuff like this for work soon, although not certain enough to talk about it.

seancorfield06:05:47

I guess I didn't know there were jobs based on stuff like that 🙂

sophiago06:05:24

The blockchain folks have finally had enough with being stolen from :woman-facepalming:

jgh08:05:20

and they’re going to start hiring people to investigate themselves? 😉

sobel11:05:15

if the blockchain folks can figure out how to stop getting robbed, they'll make a (another) fortune selling the solution

Bravi12:05:46

Hi everyone. I’ve got the following system map:

(defn new-system
  [{:keys [port mongo-uri]}]
  (-> (component/system-map
       :routes       (new-endpoint handler-fn)
       :handler      (new-handler)
       :http         (new-web-server port)
       :middleware   (new-middleware {:middleware [wrap-reload]})
       :db           (new-mongo-db mongo-uri)
       :message-repo (new-message-repo))
      (component/system-using
       {:routes       [:message-repo]
        :handler      [:routes :middleware]
        :http         [:handler]
        :message-repo [:db]})))
and this is my handler function
(defn handler-fn
  [{:keys [message-repo]}]
  (routes
   (GET "/" request (index-page message-repo))))
and it looks like my handler fn is not receiving message-repo and I can’t figure out why

Bravi12:05:43

I’m using danielsz/system for those new.. functions

annarcana12:05:33

@bravilogy You need to wrap the contents of the key in component/using and provide the key you need to the component

Bravi12:05:02

@jarcane isn’t that what component/system-using is doing though?

annarcana12:05:17

Hmm, you're right, I missed that.

annarcana12:05:33

Maybe it also needs to be provided to :handler as well`?

Bravi12:05:57

oh and also, if I do

(defn handler-fn
  [& args]
  (println args)
  (routes
   (GET "/" request (index-page message-repo))))
it shows me this:
(#system.components.endpoint.Endpoint{:routes-fn #object[component_tutorial.core$handler_fn 0x3d0697da component_tutorial.core$handler_fn@3d0697da], :message-repo #component_tutorial.models.messages.MessageRepoMongo{:mongo {}, .... 

Bravi12:05:06

notice message-repo in there

punit-naik12:05:10

Is there a library for generating mysql schema from clojure spec like the way spectomic works?

thegeez13:05:10

@bravilogy in component/system-using you need :handler [:routes :middleware :message-repo] I think

thegeez13:05:24

@bravilogy the "message-repo" in the println is the one nested in the endpoint from the :routes/Endpoint component

Bravi13:05:31

ok, I’m getting somewhere now I think 😄 so I added :message-repo to handler and it works now. but I have another issue. I’m getting com.mongodb.MongoClient cannot be cast to com.mongodb.DB

Bravi13:05:55

and I have a feeling that it’s something to do with this

(defrecord MessageRepoMongo [mongo]
  MessageRepository
  (find-all [this]
    (mc/find-maps (-> this :db :conn) "messages")))

Bravi13:05:08

this is that message repo basically

Bravi13:05:51

and -> this :db is giving me the mongo component from danielsz/system

Bravi13:05:09

but I think it doesn’t like that conn, even though that’s the actual connection

Bravi13:05:39

ok figured it out! damn it, finally 😄

Bravi13:05:04

I should’ve used (-> this :db :db) instead

Bravi13:05:29

where first db corresponds to just mapping from system-map. perhaps I should change that

sleepyfox13:05:31

Morning. I have a friend who wants to learn Clojure with TDD. I know of quite a few good books on Clojure, and quite a few books and courses that teach using TDD, but I don't know of any courses that teach Clojure using TDD - has anyone heard of one?

sleepyfox13:05:40

(and yes, I used the Slack search here and Google, and apart from a no-longer running Helsinki university MOOC I didn't find anything appropriate)

pesterhazy13:05:02

@sleepyfox the thing is, many clojurists prefer the REPL over tests for exploration

pesterhazy13:05:58

Learning through the REPL feels natural.

sleepyfox13:05:59

I would agree with the statement "learning through the REPL feels more natural than learning through tests". I was hoping that I could kill two birds with one stone for my friend, who doesn't know either. Many places now teach Ruby, Python etc. using TDD e.g. Maker's - I had wondered whether anyone had tried doing the same for Clojure.

ajk17:05:52

Perhaps it’s easier to learn TDD and programming separately (unless your friends already knows another language). I definitely think Clojure could benefit from more articles on TDD though, especially for those coming from the Java / Scala / Ruby world. Another interesting project to add to the long list of things I’d like to do!

sleepyfox13:05:56

I think there's a subtle difference here between "teaching X using TDD" and "teaching X and TDD at the same time", and I'm talking more about the latter than the former.

👍 4
mg15:05:53

@bravilogy just as a design thing, it would be better to define a function in your mongo component namespace that you call and pass your mongo component to get its database handle, rather than having other components reach into its internal structure

Bravi15:05:50

@mg the thing is, that mongo component comes from a package I use - danielsz/system and I don’t think I can extend it like that

Bravi15:05:15

but it turns out it’s not that bad. I was doing it wrong

Bravi15:05:08

but I understand your point as well

Drew Verlee19:05:36

Clojure and Datomic continue to teach me things even two years into learning the basics. 🙂. Just a reminder of how awesome this all is.

👍 12
🎉 12
mg19:05:27

Want to share what you learned today?

Drew Verlee19:05:16

I was struck with the thought today that postgres and sql dont offer declarative semantics for extending or querying arbitrary data. Specifically, if you want to associate some data in a relational model, 1.your forced to worry about the storage model (normalization vs demoralization) 2. In cases where you want flexible associations, the language offers no support, so you have to model it yourself.

Drew Verlee19:05:29

For example, if some people have dogs and others dont. You can create a 3 tables: person person_dog dogs Then use the person_dogs table to link them. The same goes for people having cats, etc…

Drew Verlee19:05:07

The information the table conveys, is no different then any other piece of information.

andy.fingerhut19:05:19

I'm pretty sure it is an unintentional typo, but I love the use of "demoralization" above 🙂

😂 8
Drew Verlee19:05:29

thats how i feel lol

Drew Verlee19:05:02

I need to find a way to make less spelling mistakes.

mg19:05:25

nice to have the ability to do [?person _ ?dog] in a query

Drew Verlee19:05:26

these issues seem minor, but if you take a big step back from what the majority of my “leads” work on, its patching issues of this nature.

Drew Verlee19:05:46

imposing graph semantics on relational databases.

Drew Verlee19:05:27

dealing with state oriented programming issues.

mg19:05:07

I wrote a bit of a screed on that former issue here: https://github.com/purefnorg/sqlium#rationale

Drew Verlee20:05:48

Also, i have been really thinking a lot about the protective mentality that types (at least in java) tend to create. So much energy goes into what cannot happen. As if the goal is to narrow the specification down to only one option. In which case, why not have a function that does that one thing? What have we achieved? We have documented what can go wrong, but we haven’t done the right thing. I think Types that model real constraints are useful, but real constraints are few and far between and many if not most are probably already handled by your programming language. This one is harder to reason about, because its not about universal truths (types are good vs types are bad) its when, in the context of your language, the type system moves you closer to a useful system or away from it. This is what happens when we get sunshine in michigan. I should move…

roklenarcic19:05:48

Hm, this is more of a Clojure/Java question, but how could I start a process in clojure that wouldn't be a subprocess in that it wouldn't exit when clojure app exits?

cjsauer19:05:06

There is most likely some kind of Clojure wrapper floating around I imagine

cjsauer19:05:46

There is also the shell namespace in clojure.java: https://clojuredocs.org/clojure.java.shell/sh

csm20:05:09

you might specifically want nohup when spawning your process

Brian F20:05:44

I just had to solve this problem recently. just adding & at the end of the command seemed to be enough. It will initially be a subprocess, but when the parent process dies it gets promoted such that it is no longer a subprocess (not an orphan)

Brian F20:05:00

One caveat I found was that when you are running this in the repl, if you ctrl+c as you are shutting it down, it will kill the subprocess before it gets promoted

roklenarcic23:05:34

hm, ok, but what about windows

Brian F23:05:07

Can't help you there, I haven't run clojure on windows yet

huthayfa20:05:02

hello guys, I want to use specter library. does anyone of you know which function is used to navigate the entire data structure tree and manipulate specific key

dadair20:05:30

See #specter

👍 4
Brian F21:05:01

Hey all, I'm currently evaluating different http servers. We're currently on http-kit, but we are trying to get off of it because it is not clear that it is going to continue to be maintained going forward. Specifically, I'm looking for something that does a good job with asynchronous websocket connection handling. Aleph seems like a reasonable alternative, but I wanted to see if anyone had any recommendations

nha13:05:46

I use #yada for that (on top of aleph)

bostonaholic21:05:39

@bfouts I’ve not used it, but http://pedestal.io might be something to look at

tanzoniteblack21:05:17

pedestal is a framework that runs on top of either jetty, immutant, or tomcat, so if you already have your own stuff built on top of a raw http server, it's probably not what you're looking at

tanzoniteblack21:05:13

http-kit is still being some-what actively maintained, so if that's your only reason for looking, I wouldn't jump the gun yet

tanzoniteblack21:05:25

but either way, aleph is also a nice choice

Brian F21:05:05

Ok sweet. Thanks for the feedback!

huthayfa21:05:05

(defprotocol process-assets (process-assets-impl [asset] "assets")) (extend-protocol process-assets String (process-assets-impl [assets] "string assets") java.util.Vector (process-assets-impl [assets] "vector assets"))

huthayfa21:05:16

whats wrong with this protocol definition

huthayfa21:05:29

I am getting CompilerException java.lang.ClassCastException, compiling

jumar11:05:14

@huthayfa.ainqawi not sure what error you're getting but this works for me

(defprotocol process-assets
  (process-assets-impl [asset] "assets"))

(extend-protocol process-assets
  String
  (process-assets-impl [assets] "string assets")

  java.util.Vector
  (process-assets-impl [assets] "vector assets"))

(process-assets-impl "my string")
;;=> "string assets"

jumar11:05:02

btw. not sure what's your intention by specifying "assets" in defprotocol definition, but that's the ends up being docstring for process-assets-impl

huthayfa21:05:26

after I cleaned the build the error was gone

dpsutton21:05:46

@huthayfa.ainqawi use backticks around your code to make it more readable. `

xiongtx22:05:51

I'm using environ to set some environment variables for a project. In one namespace there is a value like:

(def foo (Integer/parseInt (env/env :foo)))
When the ns is AOT'ed, lein {uber}jar results in a java.lang.NumberFormatException. I can set any value at compile time like:
FOO=1234 lein uberjar
And the problem goes away. But this is annoying. Is there a better way to deal w/ this than something like:
(def foo (when-let [foo (env/env :foo)] (Integer/parseInt foo)))

wiseman22:05:42

write a function (or macro) that makes it more convenient, and lets you specify what you want if the environment variable doesn’t exist.

wiseman22:05:03

(defmacro getenv [name parser default]
  `(let [name# ~name]
     (if (contains? env/env name#)
       (~parser (env/env name#))
       ~default)))

(getenv :foo Integer/parseInt 0)
;; => 0

(getenv :foo Integer/parseInt (throw (ex-info "Missing FOO" {})))
;; throws an error

ingesol22:05:12

I'm using lein ring uberwar to build my clojure/clojurescript webapp. All the clojurescript-only dependencies are copied to WEB-INF/lib, causing the war to be much larger than needed. Is there a way to only include the jvm-specific dependency jars?

gklijs06:05:25

Cljs dependencies should have the scope provided like

[org.clojure/clojurescript "1.7.228" :scope "provided"]
to prevent that.

ingesol06:05:41

Doesn't seem to work, unfortunately. All of them are still there

miro07:05:33

The "provided" scope should take care of that (did you clean properly?) Another approach is to have a separate git branch or repo for server stuff only and for production releases do an uberjar /war and just copy there the compiled js code (that you have compiled from a separate git branch/repo again).

thheller09:05:38

you can put all CLJS deps into the :dev profile via :profiles {:dev {:dependencies [...]}}

thheller09:05:11

or use a custom profile but that needs to be enabled manually during development

ingesol10:05:49

Yes, pretty sure I cleaned everything. Removed all non-git folders in project including target, out, js/compiled

ingesol10:05:36

I also thought about putting cljs dependencies in the dev profile. So the war/jar tasks don't include dependencies from dev-profile? I'm asking since the cljsbuild task implicitly does (?).