This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-06-10
Channels
- # aleph (4)
- # announcements (27)
- # aws (12)
- # aws-lambda (1)
- # beginners (207)
- # boot (4)
- # calva (8)
- # cider (9)
- # clj-kondo (9)
- # cljs-dev (27)
- # cljsrn (6)
- # clojure (104)
- # clojure-android (3)
- # clojure-dev (9)
- # clojure-finland (2)
- # clojure-italy (18)
- # clojure-spec (8)
- # clojure-uk (100)
- # clojurescript (43)
- # clojutre (1)
- # core-async (49)
- # cursive (18)
- # data-science (3)
- # duct (24)
- # events (2)
- # fulcro (27)
- # immutant (1)
- # off-topic (32)
- # om (2)
- # onyx (2)
- # pathom (14)
- # pedestal (2)
- # planck (3)
- # re-frame (38)
- # reagent (7)
- # reitit (10)
- # rewrite-clj (7)
- # ring-swagger (3)
- # shadow-cljs (32)
- # spacemacs (63)
- # test-check (16)
- # tools-deps (5)
- # vim (21)
Hey so another quick question: for error handling, e.g sending wrong request parameters or invalid operations, is it ok to return nil all the way up until the service can treat it and respond the request with 4xx? Meaning the request handlers will look something like:
(defn get-something
[params]
(let [value (get-function params)]
(if value
(ok-response)
(not-ok-response))))
and then get-function will probably be something like
(defn get-function
[params]
(let [value (try-to-get params)]
(if value
(value))))
and that will probably continue... or is there a better way to do so?I think this is reasonable, unless you want to give more specific error for different failures.
That second function could be
(defn get-function
[params]
(when-let [value (try-to-get params)]
(value)))
If you want some very simple, basic error handling/reporting, you might look at https://github.com/cognitect-labs/anomalies @hp_pedrors
Hmm I was thinking of returning an error code message, along with the value, so I can use globals and determine which message to send as well.
Anomalies supports error code (category) + message.
You could return {:ok value}
or {:cognitect.anomalies/category ... }
It's just data. You just decide on your conventions, write a spec, and off you go.
Not really pattern-matching, but I’ll sometimes do:
(let [[a b] my-vec]
(if (nil? b)
(do-thing a)
(do-other-thing a b)))
@deleted-user Just use case
Well if some-fn is overloading args, you don't even need case it'll be smart enough to dispatch on the argument count (edit, meant case, you still need apply)
^ @hp_pedrors That would be good for you to watch, based on our thread about error results.
That is, unless it is a search, but like getting a thing in a precise position will always be coupled
Also, I'm not sure about get-in, since you're right, it does a simple for of conditional with the default argument
Well, the first thing is that its a feature that complects binding
and flow control
. And Clojure likes to decomplect. So it chose to separate these two concerns.
The second thing is that pattern matching as a flow control mechanism is normally closed for extension. So Clojure offers multi-methods instead to avoid that.
You can't from outside the function, for example say, oh and by the way, if the data matches to X do this other thing.
But as a language feature, you have to think, I'm setting a norm, and is that norm likely to lead people to do things that I don't think is ideal most of the time?
Pattern matching has many libraries for it in Clojure. But Rich Hickey chose to make it more cumbersome to reach for, by not putting it in the core
To be honest, I'm not too sure myself, and now it has been a while since I've used pattern matching. Can you remind me in Elixir how you'd solve your above problem ?
> it was in core at one point, looks like Not sure what you mean there @deleted-user?
That's a Contrib library. Not part of Clojure.
Ya, I can only attribute it to wanting to encourage multi-methods over the use of case/switch
And even to some extent, protocols, though that won't apply to all pattern matching use cases
Ya, I can't say for sure. I've never worked professionally in a language with pattern matching, only toyed with those. I can't say I ever "miss" it in Clojure, but it could be because I've never used it enough.
Pattern matching is really good for a closed set of options where the compiler can help figure out whether you've matched all cases. That's what you get in languages like Scala (and Haskell I think).
Given Elixir is dynamically typed, I don't see how it can get that support -- so I can see how it would become brittle without compiler support.
@deleted-user For when you wake up... I also wonder if performance was a concern of Rich. You'll come to realize Rich Hickey is quite performance sensitive, and pattern matching has runtime overhead. So that could have been a strong reason to leave it out. Also... it was a choice for Clojure to be a Lisp with powerful extensions mechanisms. It is always okay in my opinion to reach for what you think is best, so if you really want pattern matching, a lot of people use core.match and defun (https://github.com/killme2008/defun) as well in production and what not. So don't be shy. But maybe as a beginner, just to get accustomed, it's better to start practicing ways around it, and later, you can decide if you want it back and use a library.
Which to be honest, I never understood the difference between unification and pattern matching 😛
Rich did investigate pattern matching early on. perf is one key concern (scaling with cases), and the other is the closed nature of the match set. polymorphic systems like multimethods and protocols are both open for external extension.
I also do think it complects binding and flow. For example:
(defun foo
([false true] 1)
([true false] 2)
([true true] 3)
([false false] 4))
Versus:
(defn foo [[a b]]
(case [a b]
[false true] 1
[true false] 2
[true true] 3
[false false] 4))
Much easier to refactor the destructuring fn in case the structure changes. Say it becomes a map?
(defn foo [{:keys [a b]}]
(case [a b]
[false true] 1
[true false] 2
[true true] 3
[false false] 4))
Versus:
(defun foo
({:a false :b true} 1)
({:a true :b false} 2)
({:a true :b true} 3)
({:a false :b false} 4))
As I see it, the whole point of destructuring (or one of its big use cases) is decoupling the fn body to the structure of the args. In this case, the body doesn't know how to get a and b from the arg, it's abstracted in the destructuring. But pattern marching reintroduces some of the body's logic back into it.
Does Intellij/Cursive provide auto complete for Java classes ? It doesn't work for me and I didn't find anything ? :face_with_rolling_eyes:
There's currently a bug in IntelliJ that's interfering with symbol resolution, maybe that's related? There's more discussion of the issue in the #cursive channel.
but break when i lein test
? i get exception ctual: java.lang.IllegalArgumentException: Cannot open <nil> as a Reader.
i use with-profile
so i don’t have to repeat deps. like make a :shared
profile and start the test with lein with-profile +shared test
Style question, does the &
go on the same line or last line in arguments?
(defn
[a
& b]
or
(defn
[a &
b]
I put all arguments on the same line. There should be only a few of them anyway.
In long-lived code, I always destructure in a let
inside the defn
. I have trouble visually parsing fn args that destructure inline.
in joy of clojure 2 were shown this code:
(def very-lazy (-> (iterate #(do (print \.) (inc %)) 1)
rest rest rest))
;=> ..#'user/very-lazy
(def less-lazy (-> (iterate #(do (print \.) (inc %)) 1)
next next next))
;=> ...#'user/less-lazy
However this isn't what i get in my repl:
Clojure 1.10.0
(def very-lazy (-> (iterate #(do (print \.) (inc %)) 1)
rest rest rest))
..#'user/very-lazy
(def less-lazy (-> (iterate #(do (print \.) (inc %)) 1)
next next next))
..#'user/less-lazy
has next
changed?i suppose i'm having a hard time demonstrating that rest
is more lazy then next
.
(next '(1))
=> nil
(rest '(1))
=> ()
Doesn't, to me, demonstrate laziness, there are just different return values. The claim is that rest doesn't force realization of remaining elements, but i can't build an example to demonstrate this.I think it will depend on the seq you're using
if you're digging into this area, this (historical) page from around the time of the seq overhaul may be of interest https://clojure.org/reference/lazy
() vs. nil does actually demonstrate it, but you need have more context, basically nil in this case means there for sure no more elements which means rest has inspected the contents (which is not a lazy operation). next returning () means there may or may not be more elements and next has not inspected the contents
Thanks Alex! I'll take a look.
http://insideclojure.org/2015/01/02/sequences/ may also be useful
"rest has inspected the elements" did you mean next
here?
I never bothered remembering which is which, I use rest everywhere and call seq any place I want to nil pun
ah, ok. this example makes sense to me: https://stackoverflow.com/questions/15983316/next-and-rest-in-clojure
In order to demonstrate the difference you need to make sure some other factor isn't going to eval the body and trigger the side effect (that helps show the lazyness). Here def does that, sense rest-test
points at the un examined body as where next-test
prints when called with def because it evals the body.
i suppose its simpler to use rest everywhere except when your specifically going to use the nil return value to check an exit condition? Joy of Clojure seems to suggest the opposite though 🙂 > In general, we recommend that you use next unless you’re specifically trying to write code to be as lazy as possible.
I tend not to use either of them most of the time
instead, think in manipulating colls and seqs most of the time
in case of loop/recur, lean on destructuring
there can be some very subtle implications if working with walking nth items down via next/rest but you really can ignore that 99.9% of the time
far enough. I think i understand them enough now to be aware of when the difference will matter. As you said, i mostly try to work with collections. Thanks again alex!
@alexmiller @didibus I guess I don't see why pattern matching is even being compared to multimethods and protocols, and why they're mutually exclusive. if I wanted to allow external extension I'd do multimethods and protocols (which elixir has as well), but most times I just want to do different things based on the structure I have (as in my original question) and the structures are used only within a function or namespace
Anyways, when you need a quick off the hand conditional restricted to a single function, normal conditional constructs are just fine. The danger is if you're using that same conditional logic over and over in different places, and if you are constantly having to extend it with new cases. That's when multi-methods will shine and be a better tool in my opinion
In your case. It seems what you're interested in is some kind of duck typing conditional. Like, what kind of structure do I have here. And based on that you would branch out.
Multi-methods are great for that. But they assume this will be used a lot. Like if your structures were top level entities of your app. It's overkill for you I agree
With your example, I think it's hard to understand. But in simple cases like yours, there are alternatives like you've seen. The fn overload is one, if you just want to branch on number of elements, it will also perform a lot faster.
I guess to see the difference, we'd need to have an example of pattern matching and multi-methods applied more to solve an architectural design issue.
I do think pattern marching can make some things simpler and clean and easy to read. I think the reason not to include it in the core was the fear that it would also quickly be used for larger things which multi-methods or protocols are better suited for.
So, another alternative, which I use, when your conditional logic itself is determined by the structure of data, is to use Spec
You can spec the various options using the or
spec. And then use conform to know which one it conforms too in order.
Like this:
(first
(s/conform
(s/or
:char (s/tuple any?)
:char-count (s/tuple any? any?))
[1 "c"])
Now you can imagine having a multi-method whose dispatch function is the above conform code.
After that, a conditional on count is probably second best, like with if or case if you have more then 2 branches
Ya. So I'm not gonna lie. Pattern matching in your situation also seem quite nice. I think for a lot of small simple algorithms, especially with recursive conditional branches, pattern matching is really good. And that's probably its sweet spot
And if you're doing a lot of these (and aren't trying to learn Clojure but know it well already 😉), it could make total sense to reach for core.match and defun in that scenario
I feel like I don't commonly encounter this situation
instead of applying, you could just destructure
yeah, usually the more optional part is on the right so it's not weird
@deleted-user is there anything stopping you from having the function return char+cnt
instead of cnt+char
?
you’ve uncovered a small shortcoming in Clojure’s default ability to express case matching. it doesn’t have as good of support for positional matching as, like you said as an example, Elixir
if you’re doing something that involves a lot of positional matching (like writing a parser/lexer) then you now know that you’re going to need some extra batteries if you decided to do it in Clojure
yes 😄 hopefully you also get to use some of Clojure’s stronger features like it’s rich polymorphic dispatch via multimethods and protocols
they work a lot better when combined with more descriptive data like maps and types/records
yeah. I find in applications, I tend to reach for multimethods. When writing a library, I tend to reach for protocols
Hey, can you suggest a Clojure repo that can be considered as "idiomatic"/well written? I know 101 level clojure and I want to study a repo to see if I'm actually going to like the language
many people recommend weavejester's as great example code
picking one fairly arbitrarily: https://github.com/weavejester/ns-tracker
Is there any type of problem/task to solve that helps appreciating clojures stronger sides compared to other generic langs?(idk if this is good question tho)
Clojure runs on the JVM, so it has the same pros and cons as the JVM. As a language, it has the same abilities as any other mainstream language. I would say the two of the stronger selling points are 1. code is data : which gives you the ability to pass functions as arguments and return them. 2. immutability as default: which means that hard to reason about state transitions become the exception and not the norm. I migrated from python -> ruby -> clojure and it's clear to me that everything i found useful in those languages has its roots in LISP and everything else ...
"code is data" is something else. First class functions/higher order functions is what gives you the ability to pass functions as arguments and return them.
"code is data" is what is behind macros: the ability to operate on Clojure code as a data structure -- transforming code prior to evaluation.
Lots of languages provide 1st class fns and higher-order fns these days -- you can even get most of that in Java now 🙂
And macros are not anywhere near as "popular" in production Clojure code as many people would seem to think, when they first start learning about macros 🙂
For example, from our code base at work
Clojure source 279 files 66902 total loc,
3118 fns, 641 of which are private,
422 vars, 29 macros, 60 atoms,
580 specs, 21 function specs.
(that's not including 20k lines of test code, but we don't track macros in that)
Given that Clojure is, by design, a "general purpose language", I think it's hard to point to a specific type of problem/task that shows Clojure's strengths.
It's great at data manipulation/transformation. It's great at concurrency (in various forms). Perhaps the real "killer aspect" of Clojure is a good REPL-driven development workflow? It's incredibly productive because of that extremely tight feedback loop.
Maybe its strong side is, since LISPs can look messy easily it forces me to write more fns, break-down the problem. Which is usually a good thing
REPL development (both using a actual repl or with immediate feedback using fighweel) is a big part of the "fun" here. It is hard to describe using toy examples since toy examples are usually read (not written) by those "outside" the clojure community.
Clojure is best when your state is inside a data store like S3, Mongo, MySQL, a file on disk. etc.
And your logic is mostly getting data, transforming it, putting it back, dispatching events to some other services, rinse and repeat
Clojure isn't so great for short lived programs, like grep, or other one shot quick command line tools
Or for high performance low level code, like graphics engine, audio processing, AAA games, etc.
Which is kind of unfortunate, because Clojure is better for big applications, but for small weekend projects, which is what people often play with first, it doesn't really shine. Though its definitely still good. But you won't really understand the benefits in those scenarios
Which all puts me in mind of my Clojure/West talk from many years ago "Doing Boring Things with an Exciting Language" 🙂
It's also why I feel it's good to complement Clojure with one or two more languages, if you want to cover all possible use cases.
But these will also hit a limit. So I feel Rust really covers the other half of Clojure. Or Nim, C, C++ as well.
For scripts that don't need high performance, you have ClojureScript or Joker which work very well, and are easy to pick up as they are mostly Clojure. You can even use Clojure most of the time. Like, if you don't need uttermost performance, you can generally also accept the slower start time of Clojure
Almost everything I do is server-side, long-running processes, so "all Clojure" is fine 🙂
The last bit I think is not covered is Android and iOS development. ClojureScript works for that though
But I try to follow Pragmatic Programmer advice of learning a new language every year or so... Last year was Kotlin (quite nice). Go was several years ago (disappointed). Rust was in between (liked it, but I don't do any low-level stuff these days).
Elm was another fun language to learn (several years ago).
Rust is fascinating. I haven't looked at Rebol. I did look at OCaml years ago (but ended up learning F# instead!).
Heard of Fantom. Never looked at it.
It's like how I find C# to be a better attempt at Java, and then I find Fantom to be a better attempt at C# :p
Have you tried Kotlin? That does a good job of being a "better Java".
I feel a little similar to how I feel about Scala. It tries and carve that weird middle of almost FP, but not really, almost hindley Milner, but not really
I did Scala for about 18 months for work. I liked it at first and gradually grew to like it less and less, the more I worked with it. 🙂
Hello. I'm wondering if someone can point me in the right direction to correct this log format issue. I'm using timbre for logging on Windows, and when I log an exception with timbre, I see all these characters that I think are ascii encoding or something? Is there something I can use to make the printing look as it should?
My first line of attack would be to see if you can get a terminal on windows that supports ansi colour codes, but that's outside my knowledge - if you can't do that, you can turn off timbre colour codes for stacktraces. see https://github.com/ptaoussanis/timbre/#disabling-stacktrace-colors
you can turn off aviso.pretty, that's what colorizes
Update: I found on stackoverflow that: >in latest Windows 10, you can enable ANSI in conhost via the following reghack -- in HKCU\Console create a DWORD named VirtualTerminalLevel and set it to 0x1; then restart cmd.exe.
@brandon.ringe Alternatively, maybe check the TERM value (or any cmd equivalent) to see why it thinks your term supported ANSI when it apparently didn’t. Otherwise your logs might get littered with ANSI escapes too.