> Historically, to pay your monthly bills in Brazil, you had to go to a bank branch and wait in line — often outside in the heat — until it was your turn. The lines wrapped around the block like that of an Apple Store upon the release of the latest iPhone. Being from Peru, this resonates with me 😕
What is "strict paredit" as opposed to "paredit"?
paredit is about “Structural editing” strict paredit also protecting integrity of your code disallowing deleting of closing/opening parenthesis if it introduces inbalance s-expression For examle: calva: https://calva.io/paredit/ enable strict paredit by default
the delete forward says it will prevent you from getting into an unbalanced state but the attached gif has an unbalanced close paren after a few deletions
> It is common to conflate any interactive language prompt with a REPL, but I think it is an important aspect of Lisp REPLs that they are a composition of read-eval-print. I'm not sure I understand what that means. How is e.g. JShell or IRB not a "composition of read-eval-print"?
Yes, and in IRB for example, there is no distinction between read and eval (only the latter is provided and it takes a string of code)
Right, I understand. What is the practical benefit of that from the point of view of using the REPL for interactive development, though? I understand the benefit in general (macros etc.)
For me it’s the tight iteration time that comes from working with a running program. I can inspect the working state of the program and re-define functions on the fly.
Note that most seasoned Clojurists recommend that you not usually type at the REPL prompt, but rather that you set things up so you can send forms to your REPL from your editor. You’re still just editing your program source files, but as you do so you’re sending bits and pieces over the REPL and updating your running program in real time.
Totally agreed, that's how I do all of my Clojure development. I guess what I'm really after is this: Imagine I'm a Ruby programmer who makes heavy use of IRB or Pry. A Clojurian comes along and says "Well, IRB or Pry aren't really REPLs. A Lisp REPL is a composition of read-eval-print." The Ruby programmer is like, "huh?", you know? Everything you said is true and that's already what I do, but I guess I'm just trying to figure out a way to explain the "composition" statement in such a way that someone who's used to a "non-compositional" REPL understands what I mean. I haven't used IRB or Pry etc. in a long time, but I think you can also use them to inspect the working state of the program and redefine functions, etc. You can't (easily) send things from the editor window into the REPL, but I guess that's more of a property of Lisps than Lisp REPLs, as such.
I wonder if I'm making any sense. 🙂
I guess it's just quite difficult to explain the benefits of a Clojure (or some other Lisp) REPL without actually showing what writing programs by evaluating things directly from your editor looks like.
Yeah, I’m not really sure what the “compositional” argument is about TBH.
Well, I think your initial comment was bang on.
I mean, if a Ruby dev can achieve a similar effect with IRB, does it matter whether IRB reads code differently from the regular Ruby startup? They might not find “composition” a compelling argument.
Yes, exactly!
I come from a Java background so the differences are a little more stark.
I think what you're saying makes sense, and I don't think there is any benefit to R and E being separate composable pieces, in the limited context of typing strings of code and seeing the result of evaluating it. In that sense, the irb
user is not missing out on anything.
except, as @flowthing said, macros and so on
but if you aren't modifying code as data then a REPL is a REPL even if RE are not decoupled
I would say irb
is precisely a REPL where the RE are one thing
Yes, agreed. I believe we're on the same page. 🙂
I guess I gained some new insight into this today... if you try to copy-paste multiple expressions at once into a Python shell, you get an error saying "SyntaxError: multiple statements found while compiling a single statement".
I think @jkrasnay's first comment is bang on: with Clojure (and presumably with other Lisps), using the REPL is exactly the same as running the program via other means (e.g. via a main
function or something), whereas that isn't (necessarily) the case with other languages. irb
does allow you to copy-paste multiple expressions, but it might have other limitations -- not sure.
In any case, I think that's what "composition" refers to: since reading is separated from evaluation, the REPL is guaranteed to work the same way as when reading forms from a file, whereas that's not necessarily the case with other languages.
(I'm not trying to say that JShell or IRB etc. are as good as a full-fledged Lisp REPL — I'm wondering about the "composition" bit in particular.)
Periodic reminder: there is a #contributions-welcome channel where you can ask for contributions for your OSS projects
(this channel was called #pr-welcome before)
I think the point is that read and eval are the same building blocks that the Lisp uses to run your program. This means that you can do everything from the REPL that you can do by writing source files and running your program. In other languages, you can eval expressions at the REPL, but there are certain things that are out-of-bounds, such as adding new classes in Java.
Thanks for the insight! That totally makes sense. :thumbsup::skin-tone-2:
This might be a stupid question but i really wish to understand: Why do libraries and frameworks make so much emphasis on the fact that they might have just a few milliseconds faster startup times? For example does a 3 milliseconds difference in startup time really matter? or are developers just becoming too impatient ?
@kraulain Can you give an example of this?
Off the top of my head i can see this in comparisons of frameworks like quarkus vs micronaut
The difference there isn't ms, it's probably seconds (when compiled to native which both frameworks support). And this can be a significant win in cloud environments.
Starting fast also helps if your language doesnt support a repl... so if you can changed code -> deployed fast... then you can work faster.
the mirage project got ocaml startup times so low, they can feasibly spin up a new http server on demand for a request
oops, I mean start up a container running an http server :D https://mirage.io/
You mean like it will be cheaper to run?
yes
it allows things like on demand scaling - I've worked on microservices that take multiple minutes to spin up
also think e.g. AWS Lambda where you pay per nanosecond or something like that
if it's seconds instead, you don't need to leave as much idle
Yeah, i see. That makes total financial sense
In servers, 80% of the situations the runtime speed of code don't matter because the DB is a few orders of magnitude slower. And the most remaining 20% optimization gains pennies. What remains is HFT & Google electricity.
Relevant speed gains is babashka running circles on my antique laptop 🙂
Yeah, I never thought of that. I think a single ms difference can be a big deal in High-frequency trading
both the jvm and the clojure language make multiple design decisions that trade more resource usage (read: spending more money on your containers per server) and longer startup times (read: needing to leave more servers running because "on demand" spinup is too slow), in exchange for runtime throughput (oops, the bottleneck was IO anyway...)
of course the thing clojure is really great at is optimizing programmer time (you probably spend a lot more on that than you do on hardware)
Large legacy clojure not that fun on programmer time. Immutability helps a lot, but no types is shaky business.
Not that with types is so much better... 🙂 makes changes harder.
> 80% of the situations the runtime speed of code don't matter because the DB is a few orders of magnitude slower. Right. Which is why concurrency matters (more?) Some requests can only be sped-up so much. But certainly you'd never want to intentionally slow down other requests
• CPU-bound -> multiprocessing • I/O-bound, fast I/O, Limited Number of Connections -> multithreading • I/O-bound, slow I/O, many connections -> asyncio https://luminousmen.com/post/asynchronous-programming-python3.5 (was reading this the other day)
That is because the recording is cropped. The extra paren is from the outer scope. I'll re-record it, since it does look like Calva paredit is not delivering on its promise.
ah makes sense. the paren is colored black which made it feel even more like being highlighted as mismatched rather than from an outer scope.
Yeah, the rainbows do work for parsing structure. 😃
I am trying to come up with a glob
function for a library (similar to clj-commons/fs) but there are some nuances.
The API: (glob path pattern)
Questions:
- Should the behavior be recursive by default? Some shells (bash/zsh) and languages (Python, golang) seem to have conflicting opinions on this.
- If not recursive by default, what would you make of (glob "." "**/*.md")
then, the pattern assumes recursiveness
- Should it include hidden dirs by default?
you can also refer to Tcl's implementation, might find it useful https://www.tcl.tk/man/tcl/TclCmd/glob.htm
Have you looked at the glob stuff built into java?
It’s a bit buried
yes, that's what I'm building on, but they do not provide any function like this, only the raw building blocks
Looking at ruby's https://ruby-doc.org/core-3.0.0/Dir.html#method-c-glob
• not recursive by default
• **/
makes it recur on the matched dirs
• hidden files not included by default
Generally ruby api follows unix forefathers wisdom.
Gotcha, I have written this too :)
Ish
thanks! so **/
means recursive, instead of an explicit :recursive
option right? (double checking)
Yes. Also see https://ruby-doc.org/core-3.0.0/File.html#method-c-fnmatch
perfect, thanks
That walkFileTree visitor in Java makes me so sad
So bound to mutable state
But IIRC it has hooks that the Stream-based variant doesn’t provide. Anyways, OT
Oh, well this is the off-topic room 🎸
do you mean the DirectoryStream
class? afaik this isn't like file-seq
, it only lists one level deep
it does take a glob object though, so for non-recursive globbing, I'm using that
@ghadi yeah, I mean, what Stream-based variant were you referring to?
Files.walk()