This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
- # architecture (7)
- # beginners (73)
- # boot (4)
- # cider (48)
- # cljsjs (7)
- # cljsrn (27)
- # clojure (206)
- # clojure-boston (2)
- # clojure-italy (21)
- # clojure-nl (8)
- # clojure-spec (7)
- # clojure-uk (94)
- # clojurescript (126)
- # clojutre (7)
- # core-async (3)
- # cursive (7)
- # data-science (1)
- # datascript (4)
- # datomic (6)
- # duct (1)
- # emacs (19)
- # figwheel (1)
- # fulcro (31)
- # graphql (13)
- # jobs (5)
- # jobs-discuss (42)
- # keechma (4)
- # leiningen (10)
- # luminus (3)
- # mount (2)
- # nyc (3)
- # off-topic (37)
- # om-next (3)
- # onyx (45)
- # pedestal (2)
- # re-frame (4)
- # reagent (2)
- # reitit (16)
- # shadow-cljs (118)
- # spacemacs (10)
- # tools-deps (8)
- # vim (20)
does anybody else think that functions with dynamic arity are a bad idea? Is it not simpler to just have fixed arity and currying by default?
Currying is fundamentally incompatible with variadic functions. Also, its primary purpose is for typechecking so doesn't really make sense in a dynamic language. Rich has often alluded to order in argument lists as being inherently problematic, which I very much agree with (I don't with everything he says), but currying just reifies that rather than providing a solution. Before I even thought of it this way, I had a vague intuition based on practical issues I encountered and then found @U064X3EF3 saying the same thing in a blog post: essentially that in most cases functions should either be unary or variadic. They generally compose better.
that's my whole point. On the one hand the freedom to define variadic functions is well, freedom. on the other hand, we are giving away a lot of power when we choose not to know the count of function arguments. we could change the order of arguments by combinators, that's a non-problem. I strongly disagree with your point about composition. For instance, I suspect transducers were introduced as a "special" currying mechanism precisely for the sake of composition.
"idiomatic" clojure functions have a strong bias towards collections. people will pay for this bias.
@UA2JG2Y11 what do you mean by "dynamic arity"? Do you specifically mean the
& more notation, or do you mean any function with more than one arity?
Well, there are plenty of core functions that work that way and their usage is considered idiomatic Clojure...
How would you prefer to write a
map that could operate on two collections? Or three collections? Or...?
So instead of
(map f coll1 coll2 coll3), you'd want people to write
(map f-triple (some-fn [coll1 coll2 coll3])) where
f-triple accepted a single argument containing a vector of three elements? And
some-fn would be what? It would need to be something that took an arbitrary collection-of-collections and produced a collection-of-tuples?
Instead of computing pair-wise numeric difference by doing
(map - numbers1 number2) I'd have to write a
diff function that accepted a pair and returned the difference between its first and second element. And I'd have to pay the overhead of walking through both
numbers2 and zipping them together before I could
map my new
diff function over them? Seems like a lot of additional (and unncessary) work...
(`some-fn` would need to be a unary version of
interleave -- and would need to be lazy so that you could still
map over infinite lazy sequences)
i want people to have maps that are curried by default and compose instead of having transducers.
Wow, just returning to this. Can I intervene as someone whose two primary languages are Clojure and Haskell?
@UA2JG2Y11 All I'm saying is that languages make different choices -- you can't change horses mid-stream. The decision was made ten years ago.
They are both perfectly valid choices. But once the choice is made -- and production code depends on that choice -- you can't change it.
Currying is not by any means an independent choice from everything else in language design. Imo it's almost necessary for Haskell and equally almost purposeless in Clojure.
@U2TCUSM2R I agree (but that's a more subtle argument -- the basic choice of currying vs variadic, once made in a language, can't be changed).
Well, as I noted initially you quite literally cannot curry a variadic function. So, yes, it's totally ingrained.
But you also can't typecheck a variadic function, at least using any common methods I'm aware of.
This discussion also seems to have shifted to built-in higher order functions vs.
defn. In the former context the fact that dynamic typing allows them to be variadic (although it seems few other dynamic languages take advantage of this?) is a huge win.
Like just think about the existence of a function called
zipWith7 in your standard library. Really think about how nuts that is.
Someone decided long before Clojure was even created that seven lists was a reasonable number to stop at 😛
Using a practical example, I literally had a coworker who generated generic tuple instances for a Haskell Postgres library using the C preprocessor. He stopped at 26 for the obvious reason.
I lied. At least one more point: the pointfree style you get from currying is replicated in Clojure through one very simple macro anyone could have implemented themselves:
@U2TCUSM2R At the risk of hijacking this thread further... as a Haskeller, what do you think about the use of monads et al in a language like Clojure? We have
algo,monads but it's very rarely used. I've written monadic code occasionally with Clojure (my Engine OSS library was in that style) but pretty much every time, it ends up feeling very non-idiomatic and clumsy in Clojure.
I have a strong opinion that they're useless. I mean, there are forms we all use that just happen to be monads but that's not really significant to even think about. And fwiw, I looked around a bit in
algo.monad a while back and wasn't interested by anything. I remember I was using ad hoc continuations in a cljs library at the time and imo they were better than the continuation monad in that library.
There's also the issue of how monads are just so ridiculously horribly taught. The type we use in programming, as opposed to category theory where they're actually quite an advanced concept, should actually be incredibly intuitive to Lisp programmers.
If you're at all familiar with continuation passing style or have written a simple lisp interpreter with lexical scope, just think of the additional environment variable. That's your continuation and the continuations monad is a "strong monad" from which the familiar "weak monads" we tend to use in Haskell can all be constructed (this is somewhat related to "Hask," the category of Haskell types, being a bicategory or "weakly-enriched 2-category...this is one reason the ML folks bash on Haskell).
Then you can think of Reader as just reading from the environment, Writer as writing to it, and State as a restricted version of Writer. IO is actually not a monad, it's compiler magic they make a monad to compose with the actual ones. That obviously doesn't help with explaining this stuff...
I've also grown to use monads in Haskell differently than many Haskellers. Michael Snoyman talks about this pattern a lot and it should be already familiar from using atoms/agents/refs in Clojure. Monad transformers are really obnoxious so it makes sense to consider the necessity of each one. StateT is technically pure, but has all the problems of mutability with a ton of added complexity just to say it's pure. Instead you can just use an IORef/MVar/TVar in a MonadIO context (I default to MVar, which is probably the closest to a Clojure atom). Then ReaderT handles tracking these actions between functions without actually modifying the environment.
Actually, though, I just spit all that out because I'm writing a blog post where I tack that bit on at the end as a half-joke: "btw, you just read a monad tutorial"
(sorry, got distracted by TV 🙂 ... yeah, it's been a long, long time since I did CPS, category theory, and all that stuff -- like maybe 35 years?)
My main academic FP work predates Haskell. My PhD in FP language design and implementation was mid-80's 🙂
Cool. I never knew you had an academic background (I suppose it says something I assume not by default in these parts). And in the UK too so that's extra serious: the Romans had to build a wall to keep the type theorists out.
Just that you pass arguments to functions in a given order, which I find inherently problematic since it's not first-class. You can make it first-class, though, with
apply, which is dependent on the allowance of variadic functions (since they represent arguments as collections, just as they are syntactically as well as in compilation). Or just rest args, which are literally what you consider harmful. Currying doesn't make them first-class; its primary purpose is for typechecking.
what does reify exactly do in clojurescript? (I’m trying to puzzle through https://github.com/funcool/httpurr/blob/master/src/httpurr/client/xhr.cljs#L62)
the docstring says a lot about reify without ever saying what it’s for. in particular I cannot parse this sentence “recur works to method heads The method bodies of reify are lexical closures, and can refer to the surrounding local scope”
@lee.justin.m Is your question ClojureScript-specific? In general, it lets you create an object that implements a protocol, on the fly (as opposed to employing
I think the “recurs works to method heads” fragment probably needs some copyediting, but in general if you
recur in the implementation of a protocol method, without a
loop, it goes where you’d expect (the method head, just like it would if you were using
recur in a function, with the only trick being that you don’t pass the “this” first argument in your
@mfikes thanks much. I have had the hardest time wrapping my head around
defprotocol. The basic gist of this is that you end up with an object-like thing that allows you to use the dot-accessor method invocation (whatever that is called). For example once this particular piece
reify returns something that you can do
(.send result-of-reify) because the protocol being implement here requires a
It works more like regular Java / OO polymorphic dispatch (on the type of the first argument)
okay right. thanks. that makes sense here since they are implementing an async network call in different ways, and then the top-level function internally does a
(-send client ...) and it’s your job to hand it the right client to that top-level
reify above with this
(deftype X  ICounted (-count [_] 10)) (let [x (->X)] (-count x))
Incidentally, if you call
count on an object in ClojureScript, it will employ a mixture of
cond-based dispatch (for example, if it it satisfies
string? it will do
.-length) and protocol based dispatch, calling
-count, which will then do the right thing based on the myriad
ICounted collection implementations, as well as
If you are curious about that bit about
recur and dropping the implicit
this, here is a post http://blog.fikesfarm.com/posts/2017-06-27-clojurescript-method-head-recur-in-defrecord.html
different question: when one uses the
(:import [ syntax, is that
XhrIo object constructed already? in the
cljs-http library, they use the same object and call a constructor on it first (https://github.com/r0man/cljs-http/blob/f4b3e6b7d8bef925741b132801282a5469ac54c4/src/cljs_http/core.cljs#L53) but in this library I swear they never do that (https://github.com/funcool/httpurr/blob/8b3e23240c6c4e2a03dcae6f7a9af4fce6b98476/src/httpurr/client/xhr.cljs#L12). It doesn’t seem like that could work unless the import syntax works differently than I think it does.
XhrIo object is already constructed; imports can only import an existing object. the thing you're missing is that this object has both static and instance methods: https://google.github.io/closure-library/api/goog.net.XhrIo.html
Hi there, I’m having trouble figuring out how to run tests with
midje.repl while excluding a specific subset of them. For example, with
lein-midje I can run
lein midje :filter -smoke to run all tests which don’t have
:smoke specified in the test metadata (this works well).
As far as https://github.com/marick/Midje/wiki/The-compendium describes, I should be able to replicate this behaviour using
midje.repl by running
(load-facts :all (complement :smoke)). However, it just goes ahead and runs all the tests, including the