[HN Gopher] REPL vs CLI: IDE wars
       ___________________________________________________________________
        
       REPL vs CLI: IDE wars
        
       Author : vlaaad
       Score  : 117 points
       Date   : 2021-07-01 13:46 UTC (9 hours ago)
        
 (HTM) web link (vlaaad.github.io)
 (TXT) w3m dump (vlaaad.github.io)
        
       | godshatter wrote:
       | > Don't forget the set -euo pipefail at the beginning of your
       | script. What does -euo means? I don't know, I copy-pasted it and
       | man set said there is no manual entry for set.
       | 
       | Use "man bash" to find the list of built-in commands. Scroll
       | _way_ down, or search for  "SHELL BUILTIN COMMANDS".
       | 
       | The "e" command-line switch to the set command tells the script
       | to exit immediately if there is a non-zero return value. The "u"
       | switch tells the shell to treat unset variables as errors when
       | performing parameter expansion. The "o" switch enables the
       | following option (in this case "pipefail"). "pipefail" tells the
       | script to return the value of the rightmost command that returned
       | with a non-zero value in a pipeline. This is all paraphrased, the
       | details are in the man page.
        
         | AceJohnny2 wrote:
         | Off-topic, but Bash's manpage is awful and a great
         | demonstration of the limitation of manpages.
         | 
         | Not that the content is bad (it's great!), but as OP (and you)
         | demonstrated, finding relevant information is close to
         | impossible in the humongous document.
         | 
         | In Bash's defense, its manpage is a concession to people's
         | habits, and its documentation really rests in its Info page.
         | Except that doesn't seem to be popular either (I'll admit to
         | being still inexperienced with its UI, after decades of sparse
         | usage)...
        
           | AceJohnny2 wrote:
           | Perl (and Git and TCL) seem to have found a middle ground,
           | where they broke up their manpages into different ones that
           | refer to each other (via "SEE ALSO" section at the bottom).
           | But it's sad that, in 2021, we still have such a poor
           | terminal documentation system, and the improvement invented
           | 40 years ago (Info) gained no steam...
        
         | ajkjk wrote:
         | It's kinda nonsense that `man set` doesn't provide that
         | information. But it's kinda nonsense that those flags aren't
         | the default, anyway.
        
           | aidenn0 wrote:
           | "man set" can't provide that information trivially without
           | knowing your shell; dash/bash/ksh/zsh will all be subtly
           | different.
           | 
           | However, if you are running bash, "help set" is fine.
           | 
           | FWIW, I really don't like pipefail as a default (e.g. piping
           | to "head" causes random pipefails) and -e also has some
           | confusing semantics. Also either failglob or nullglob are
           | more important than either and -C is useful for some scripts
           | as well.
           | 
           | [edit]
           | 
           | Simple example of confusing "set -e" semantics:
           | 
           | The following does not print "hi" and shows returns error
           | status, as expected:                 (set -e; echo hi); echo
           | $?
           | 
           | But put it in an if statement, and it's suddenly success, and
           | _does_ print hi:                 if (set -e; false; echo hi);
           | then echo hello; fi
           | 
           | The same problem applies to functions. The below function
           | will remove all files in the current directory if it's called
           | from a conditional, but not otherwise!                 foo()
           | {         set -e         cd /some_directory # set -e means we
           | exit if this fails         rm -rf *       }
           | 
           | I think just defining a die() function and using it after any
           | command that must succeed is more verbose, but less error
           | prone:                 cd /some_directory || die "chdir
           | failed"       rm -rf *
        
             | drran wrote:
             | > (set -e; echo hi); echo $?
             | 
             | It works in my shell. :-/ It looks like you forgot to
             | insert `false` command.
             | 
             | You are pointing to the problem with -e not working in
             | subshell/deep functions, because of POSIX. Right? It's
             | described in bash documentation:
             | http://www.gnu.org/software/bash/manual/html_node/The-Set-
             | Bu...
             | 
             | > I think just defining a die() function and using it after
             | any command that must succeed is more verbose, but less
             | error prone:
             | 
             | Yep. It's the style I developed 12 years ago, when working
             | at Bazaarvoice, when I was lead of devops team. I created
             | the whole library for bash, to use this pattern
             | consistently. See https://github.com/vlisivka/bash-
             | modules#error-handling
        
       | smw wrote:
       | I think you might be missing the main "aha!" of clojure REPLs vs
       | REPLs in non-homoiconic languages: you can very easily execute
       | small parts of the program you're editing without re-typing the
       | code.
       | 
       | In emacs, for instance, you often use `eval-last-sexp`, by
       | default bound to C-x C-e. This lets you move your cursor to a
       | particular point in the file, often deep in a function, and get
       | the results of just the form(s) you're pointing at.
       | 
       | This is a superpower! It lets you test small pieces of your code
       | without writing any test harnesses or scaffolding. Try it with an
       | editor that embeds the repl and has these commands, and you'll
       | never want to develop any other way.
       | 
       | It does cause you to want to structure your code in 'repl-
       | friendly' ways, so that you can, say, restart your main server or
       | reset your state without restarting the whole process.
        
         | dragonwriter wrote:
         | Homoiconicity isn't required for that, all you need is to be
         | able to map between source and AST enough to identify
         | expression boundaries.
         | 
         | Plenty of development environments for non-homoiconic languages
         | provide the ability to evaluate a selected expression (not just
         | the last one) in a linked REPL on demand.
         | 
         | I love the Lisp family, but I don't know why its advocates
         | often sound like they haven't seen a dev environment for a non-
         | Lisp language since the early 1980s when pointing to "unique"
         | advantages of Lisps.
        
           | elamje wrote:
           | Maybe it's not required. However, I can confidently say
           | Clojure in emacs feels like an entirely different development
           | experience than my daily Python work in PyCharm/VSCode and C#
           | in VS. I recommend trying it out if you haven't already.
        
             | [deleted]
        
             | pjmlp wrote:
             | And I recommend trying out Smalltalk.
        
           | marcosdumay wrote:
           | Homoiconicity is an ill-defined concept. If you take every
           | subjective feature from it, all it implies is that you are
           | able to cut your code into well defined AST structures (what
           | all languages have, but for some it's easier than for
           | others).
           | 
           | So, yeah, anything people do with Lisp could be done for any
           | other language, it only requires more complex tooling. How
           | much more complexity depending on the language, and it varies
           | from "barely perceptibly more" into "that's completely not
           | practical".
        
             | js2 wrote:
             | It's a well-defined concept in theory but in practice it is
             | not:
             | 
             | https://www.expressionsofchange.org/dont-say-homoiconic/
             | 
             | https://news.ycombinator.com/item?id=20657798
        
           | pjmlp wrote:
           | Not only that, Smalltalk, Mesa and Mesa/Cedar workstations,
           | all shared the same interactivity as Interlisp-D at Xerox
           | PARC.
        
           | leephillips wrote:
           | Indeed; I do this every day with Vim and its term command,
           | with Julia, Python, bash, Elixir--it works with any language
           | with a REPL of any kind.
        
             | nlitened wrote:
             | I am not familiar with how Julia and Elixir work, but form
             | my experience Python's REPL is not the same at all as REPL
             | in Clojure or other lisps.
             | 
             | In Clojure, you literally built your program in memory
             | while you're writing the source code file, updating the
             | memory representation of your running program part-by-part
             | without ever stopping it.
             | 
             | This experience is absolutely magical, and it's very hard
             | to go back to edit-compile-run loop in other languages
             | after that. It is completely different from "my language
             | has a REPL and I can execute parts of code in it".
        
               | leephillips wrote:
               | No, Julia and Elixir are the same as Python in that
               | regard (Python is more annoying because of the
               | indentation problem). I was just talking about
               | interactive evaluation of expressions selected in the
               | editor. Although I've messed with Clojure a bit, I admit
               | I don't fully appreciate what you're describing. I'm a
               | little afraid to find out, frankly, because it sounds
               | seductive.
        
               | shele wrote:
               | Julia is much closer to Clojure than to Python in this
               | regard, which brings back the point from above that
               | homoiconity isn't the key ingredient
        
               | leephillips wrote:
               | How so? In julia, one problem is that you can't redefine
               | datatypes in the REPL. You need to restart.
        
               | shele wrote:
               | Yes, that restart is painful exactly because it
               | interrupts the "built your program in memory while you're
               | writing the source code file" OP talks about.
        
               | utxaa wrote:
               | my inkling is that the more special forms the more
               | difficult it is to repl a language. if everything is an
               | expression not only can you eval parts of it, but that is
               | enough to support quite a bit of extensibility sans
               | macro.
        
               | [deleted]
        
           | dgb23 wrote:
           | There is a significant, qualitative difference between using
           | a language that designed for REPL use and one that isn't.
           | 
           | Those boundaries you talk of are the crux of the issue. A
           | highly dynamic, completely expression based language is going
           | to enable a much different experience. Homoiconicity also
           | plays an important role here, because you can ispect and
           | parse code within the language, with the same functions and
           | algorithms as everything else.
        
         | tomrod wrote:
         | This is the draw for Jupyter Notebooks as well. Execute code
         | blocks. I remember when I learned this in Matlab.
         | 
         | REPL are super nice for this!
         | 
         | I've had a hard time in Java, Fortran, etc. in determining the
         | right development patterns that are used in this space to be
         | productive.
        
           | bookofsand wrote:
           | Step1: Write a test.
           | 
           | Step2, IDE: Right click + Run.
           | 
           | Step2, CLI: $ run <test name>
           | 
           | Bonus: The test is now part of your automated test suite, and
           | will be ran many times to ensure things don't break.
        
             | dec0dedab0de wrote:
             | What if you don't know what you want it to do yet? Maybe
             | you have a new API or library you're working with and the
             | documentation is lacking. Maybe you're exploring some
             | dataset trying to figure out what kind of information can
             | be gleaned from it.
             | 
             | Maybe it takes 3 minutes to get it into the state where it
             | failed, so instead of starting over every change you just
             | want to modify the one function over and over.
             | 
             | Doing it in a repl like jupyter has you run the actual code
             | many times as you're writing it so ensure things don't
             | break before you ever save the file. Of course thats not an
             | excuse to skip tests, but if I was going to pick one, I
             | would choose running the code as I write it, instead of
             | testing it after the fact.
        
               | bookofsand wrote:
               | A. Then don't submit the exploratory test in your next
               | PR.
               | 
               | B. Then memoize the expensive function to disk. Bonus:
               | Can keep multiple versions on disk, thus can tweak the
               | expensive function while having fast reloads with
               | guaranteed correct version.
        
             | pjmlp wrote:
             | Now try to fix the code from the unit test when it breaks
             | and redo the unit test, from the point where it broke.
        
               | mypalmike wrote:
               | Throw a breakpoint in the unit test.
        
               | pjmlp wrote:
               | It won't work on the CLI version, and unless you are
               | using an IDE with a backtracking debugging it won't help.
        
           | cbm-vic-20 wrote:
           | Modern Java (11+) has jshell, which has interactive goodies
           | like tab completion, documentation display, paren/bracket
           | matching, etc.
        
             | utxaa wrote:
             | it's something.
        
         | simiones wrote:
         | The homoiconicity of Lisp is not what gives it this superpower.
         | It is rather Lisp's dynamic nature that actually allows you to
         | keep running your project while also developing it that is
         | crucial - something that Clojure running on the JVM can
         | actually only approximate.
         | 
         | For example, in a full CL, you can modify a class that is
         | currently being used, and objects of that class will adjust
         | accordingly (for most modifications). This is not possible for
         | the JVM - to modify a class, you would have to unload it, which
         | can only happen if the Class object is garbage collected, which
         | requires all instances of the class to be GCd, and the
         | ClassLoader that loaded that class as well - only then could
         | you add a field with a default value to the class.
        
           | utxaa wrote:
           | i get the magical aspect of repls in scheme and CL. not in
           | clojure. not sure why. so i don't like clojure, but i feel
           | like i should ?? why is that?
        
             | tsimionescu wrote:
             | As I said, I think Clojure is significantly iimpacted in
             | how much dynamic reconfiguration it can handle by compiling
             | down to the JVM.
             | 
             | Still, personally I have little to no experience with
             | Clojure, so I can't really comment too deeply.
        
         | fnord77 wrote:
         | I can do this in Java and C with the right IDE.
        
           | utxaa wrote:
           | i don't love java but no one can argue with the fact that it
           | has fantastic tooling.
           | 
           | properly written java is a joy.
        
         | winrid wrote:
         | You can do this in IDEA's IDEs too, just place a breakpoint and
         | then you can "evaluate expression" in that context to call
         | functions, read values, etc.
        
           | keymone wrote:
           | you just have to try to understand the difference. the
           | friction is zero in a proper repl, unlike breakpointing and
           | evaling.
        
           | fnord77 wrote:
           | I remember doing this with emacs and C in the 90s
        
             | winrid wrote:
             | Nice. I definitely want to give emacs a solid try at some
             | point.
        
               | utxaa wrote:
               | i've used emacs for 20 years. i still spend 80% of my day
               | in it ... and vscode is vastly superior for development.
        
           | BoxOfRain wrote:
           | Kotlin and IntelliJ IDEA really are a joy to work with in my
           | opinion, they're very much my primary tools at the moment.
           | I'd never really been exposed to functional programming
           | features until I got into Kotlin and now I'm learning Scheme
           | of all things, it's been a bit of a gateway drug for me.
        
         | lennoff wrote:
         | When you evaluate a form deep in a function, how do you handle
         | variables that are defined "outside"? How can you evaluate such
         | expressions?
        
           | Scarbutt wrote:
           | By evaluating outer forms first.
        
           | slifin wrote:
           | Cursive has a repl powered debugger just put your breakpoint
           | there use the repl to trigger the call then use intellji's
           | debugger to step through
           | 
           | If you want to run expressions in context just use the
           | expression editor like a normal repl
           | 
           | Something I haven't tried yet is redefining functions at
           | debug time can't see why it wouldn't work though
        
             | notriskfree wrote:
             | Smalltalk famously let you code inside the debugger. You
             | can run code with methods that don't exist yet and add them
             | in the debugger when it notices they are missing.
        
           | synthc wrote:
           | Cider (the clojure plugin for emacs) has a variant that
           | prompts you for the the values for those variables.
        
             | utxaa wrote:
             | hmmm ... what if it's a closure, and it depends on a huge
             | lexical environment?
        
             | eckesicle wrote:
             | Which function is that? I couldn't find it in the docs
        
             | weavie wrote:
             | Oh nice. That's one place where Clojure beats Common Lisp
             | then..
        
               | PuercoPop wrote:
               | Does it? Asking for a value when encountering an unbound
               | variable is a default restart                   $ sbcl
               | This is SBCL 2.1.1.52.HEAD.321-f8a57bcca, an
               | implementation of ANSI Common Lisp.         More
               | information about SBCL is available at
               | <http://www.sbcl.org/>.              SBCL is free
               | software, provided as is, with absolutely no warranty.
               | It is mostly in the public domain; some portions are
               | provided under         BSD-style licenses.  See the
               | CREDITS and COPYING files in the         distribution for
               | more information.         \* (\* x x)
               | debugger invoked on a UNBOUND-VARIABLE in thread
               | #<THREAD "main thread" RUNNING {1001860103}>:
               | The variable X is unbound.              Type HELP for
               | debugger help, or (SB-EXT:EXIT) to exit from SBCL.
               | restarts (invokable by number or by possibly-abbreviated
               | name):           0: [CONTINUE   ] Retry using X.
               | 1: [USE-VALUE  ] Use specified value.           2:
               | [STORE-VALUE] Set specified value and use it.
               | 3: [ABORT      ] Exit debugger, returning to top level.
               | (SB-INT:SIMPLE-EVAL-IN-LEXENV X #<NULL-LEXENV>)
               | 
               | 0] 2                   Enter a form to be evaluated: 3
               | 9         \*
        
               | weavie wrote:
               | Actually, yes I seem to remember it doing that a while
               | ago. I think my Emacs setup has messed up somehow. Now I
               | just get:                  Debugger entered--Lisp error:
               | (void-variable k)
               | 
               | And then a stack trace. No option for restarts.
               | 
               | It works if I run from the command line though..
        
               | fiddlerwoaroof wrote:
               | Yeah, in fact, Clojure needs special tooling for this,
               | while it's built into CL's execution model.
        
           | weavie wrote:
           | You can essentially give values to global variables with same
           | name as those outside variables.
        
           | notriskfree wrote:
           | I suspect you are right. Rebinding an inner function inside
           | another function that contains variables probably will not
           | work. But you could still alter and rebind the entire thing.
           | And that is still flexible; you can still change a running
           | program on the fly with only that ability. And you can always
           | reference objects at the top level.
        
           | Graziano_M wrote:
           | The repl maintains an environment.
        
           | dgb23 wrote:
           | You can use a REPL powered debugger. You can interact with
           | the debugger to a higher degree than with most mainstream
           | languages. It's not quite as powerful as CL though, from what
           | I've read.
           | 
           | Or you pull out expressions and test them in isolation with
           | given (assumed) inputs.
        
         | utxaa wrote:
         | > In emacs, for instance, you often use `eval-last-sexp`, by
         | default bound to C-x C-e. This lets you move your cursor to a
         | particular point in the file, often deep in a function, and get
         | the results of just the form(s) you're pointing at.
         | 
         | but that depends on how the environment is looking at the time.
         | 
         | during development, in scheme, i just have a function (reset)
         | that reloads all the files of an app. sometimes i create a
         | thread and poll the filesystem and reload everything that
         | changed in the last second. that way i never have to C-x
         | anything. if i need to eval a subexp i just jump on the repl
         | and do it.
        
       | a-dub wrote:
       | protip; you can bring this flow to any language with a repl using
       | vim-slime and a terminal manager like tmux.
        
         | revscat wrote:
         | That was my impression. I've been doing this for years with
         | Ruby, tmux, and some custom zsh widgets.
         | 
         | https://github.com/jchilders/dotfiles
        
       | geokon wrote:
       | Thanks for introducing me to `add-lib` This is going to be a huge
       | time saver :) More info here:
       | https://insideclojure.org/2018/05/04/add-lib/
       | 
       | Hopefully there will be some way to just "reload" your whole
       | `deps.edn` though
       | 
       | "If you get an error, the execution stops by default and you get
       | a stack trace."
       | 
       | I'd say the other missing piece of REPL development is that while
       | you get a stack trace, you don't get a program state like you do
       | with GDB or ELisp. Maybe I'm "holding it wrong" but this causes a
       | lot of friction and lost time. I'd be curious how others approach
       | this. And that all being said, the CLI doesn't relaly offer a
       | better alternative here.
       | 
       | For entirely replacing the CLI I think that since Clojure is a
       | general purpose language there ends up being a tad more boiler
       | plate than you'd like.. It's not at the point where you're gunna
       | just run `clj`, load in some library with `add-lib` and start
       | messing around b/c things are just a tad too clunky.
       | 
       | For instance if you wanna read in a CSV file (I had to look this
       | up)                   (-> "my-csv-file.csv"         (io/file)
       | (.getCanonicalPath)         (io/reader)         (csv/read-csv
       | :separator \,)         (#(into [] %)))))
       | 
       | Uhh.. so you're prolly gunna want to wrap that up in a helper
       | function. I personally end up making a dummy "project" where I
       | keep a bunch of helper functions and then doing my REPL
       | "scripting" and messing around in that. It feels a bit wrong..
       | but at least to me it looks like a solvable limitation. Given a
       | nice set of helper libraries you could probably get to a point
       | where a bare `clj` REPL would be as ergonomic as a more
       | explicitely interactive language like R/MATLAB/etc.
        
         | marcosdumay wrote:
         | > Maybe I'm "holding it wrong" but this causes a lot of
         | friction and lost time.
         | 
         | It is a completely non-problem for functional code. It is a big
         | problem for imperative code.
         | 
         | You can't just write all of your code in a functional style,
         | but depending on what you are doing the limit gets larger or
         | smaller. So it's normal that this will be a showstopper for
         | some people, and irrelevant to others.
        
           | geokon wrote:
           | I don't really follow.. How is it a non problem in functional
           | code? For instance you have some recursive idempotent
           | function that blows at some point. Wouldn't you wanna see the
           | state at which things broke?
           | 
           | Just because things are functional doesn't mean you always
           | knows the inputs at all times
        
           | simiones wrote:
           | What does this have to do with imperative state?
           | 
           | Take this program:                 compute x = 1 `div` (x -
           | 3)       map compute [1,2,3,4]
           | 
           | Would seeing that it crashed in `compute` in `map` be enough
           | info to debug, or finding out that `x` was 3 when it did also
           | help?
           | 
           | Edit: fixed to use integer division so we actually crash .
        
         | dghf wrote:
         | This should work just as well:                   (csv/read-csv
         | (slurp "my-csv-file.csv"))
         | 
         | Or if you prefer the threading-macro style:
         | (-> "my-csv-file.csv" slurp csv/read-csv)
        
           | geokon wrote:
           | Oh thanks. Yeah. I thought maybe I was noobing it up :)
           | 
           | Always nice to learn something new
        
         | thom wrote:
         | CIDER ships with a perfectly adequate debugger if you want it.
         | Might require a bit more manual labour than some environments
         | but you're never stuck just staring at a stack trace if that's
         | what's bothering you.
         | 
         | I think using external dev dependencies and rich dev/user.clj
         | files is pretty common on Clojure projects. Your REPL isn't
         | just somewhere to interact with the current codebase directly,
         | it's a framework for building that software and managing its
         | environment more generally.
        
       | jefurii wrote:
       | Linked to in the article: UNIX as IDE
       | (https://blog.sanctum.geek.nz/series/unix-as-ide/)
        
       | tomconnors wrote:
       | This post could be summarized as "write as much of your project's
       | tooling as you can in the project's main programming language and
       | the project's main programming language should be Clojure" and I
       | agree wholeheartedly.
        
         | toomanybeersies wrote:
         | The general gist of the article also applies to Ruby too.
        
         | blacktriangle wrote:
         | I think their point is bigger than that. Historically one of
         | the major points against using Clojure to write your tooling
         | was the slow startup times which are just painful from the CLI.
         | It looks like the clj-exec idea linked to in the article is the
         | secret sauce that makes moving to writing your tooling in
         | Clojure a workable idea, since now you have a unified calling
         | convention for both calling tooling from the REPL during
         | development but also activating your tooling from the CLI in
         | some CI or build pipeline where the massive Clojure startup
         | times don't matter.
         | 
         | This article comes at a perfect time, we're just starting a new
         | Clojure project and were looking into how to automate tooling
         | since we'd been burned by Clojure startup times before. Looks
         | like clj-exec means we can now unify our work on Clojure.
        
           | Borkdude wrote:
           | Also check out https://babashka.org: it offers Clojure
           | scripting with very fast startup time. It also has a task
           | runner (similar to make, just, etc.) that can be used to
           | store long invocations (like clj-exec tends to have).
        
           | utxaa wrote:
           | what about the horrible debugging experience?
        
       | lateusername wrote:
       | Considering its ecosystem, what do most here use Clojure for or
       | what do you think is its sweet spot? I used it in a project with
       | pg/ring/reitit and while some things were nicer it was not better
       | enough to make me switch from pg/nodejs/express for new projects.
       | Used emacs with inf-clojure (found cider too bloated and buggy,
       | was tempted to port some features of cider to a fork of inf-
       | clojure) and macros were helpful in a couple of places, but other
       | than that, you can write JS almost as how most use Clojure. Also
       | found JS to be faster unless you got out of your way to write
       | Javaish Clojure, which reminds that is was a time sink having to
       | deal with Java libs(it's really not as "seamless" as most
       | advertise) because of no Clojure equivalents.
        
         | bcrosby95 wrote:
         | Nowadays I use Clojure as my primary general purpose
         | programming language.
         | 
         | As far as sweet spots go, due to pervasive immutability, I
         | think it can be a good choice anytime you're dealing with
         | concurrency.
         | 
         | If you're dealing with relatively straightforward web
         | applications, outside certain specialized scenarios (and
         | certain really bad choices) I don't think language choice
         | matters.
        
         | blacktriangle wrote:
         | Clojure's sweet spot is very much the "situated program" Hickey
         | likes to talk about. Clojure is a great fit for line of
         | business apps where maps really are the right choice for
         | modeling your domain.
         | 
         | We recently evaluated Clojure vs node for an upcoming project
         | and went with Clojure because while most of the work really is
         | just querying a db and writing JSON, we also do a fair amount
         | of heavy reporting and pdf generation which will bring node to
         | a crawl relatively speaking. Rather than have to take on the
         | operational complexity of microservices for the slower parts of
         | our app we just went with a Clojure monolith. The draw of a
         | single language to work with for both client and server was
         | very tempting, but ultimately the JVM won out on the server.
         | 
         | I'd be amiss if I didn't also say the whole Deno situation has
         | us worried about the long-term implications of spinning up a
         | brand new node project, where the JVM seems far more reliable
         | logistically.
        
       | dgb23 wrote:
       | Some Clojure frameworks and libs do provide REPL support for
       | things that we would otherwise expect to be CLIs, such as running
       | migrations, (re-)starting services and so on.
       | 
       | The startup time criticism is valid, but in context of
       | development you typically don't restart it except you pull in
       | deps (very rare) or something terrible happens (rare).
       | 
       | Then, there is also borkdude/babashka which is a Clojure powered
       | scripting tool with fast startup times due to GraalVM.
        
       | airocker wrote:
       | REPL will not work well with multitheading/futures in Clojure.
        
         | utxaa wrote:
         | or the ridiculous namespace system.
        
       | [deleted]
        
       | mgiannelis wrote:
       | I want to publish thois theory on tech business news
       | https://www.techbusinessnews.com.au
       | 
       | How would I go about adding this to a feed?
        
       | dig1 wrote:
       | > I use add-lib branch of tools.deps.alpha that allows me to add
       | dependencies dynamically at the REPL and then start using them
       | immediately, just like in the shell.
       | 
       | You could do this with (battle-tested) pomegranate [1] ages ago,
       | which is used by leiningen as the default resolver.
       | 
       | [1] https://github.com/clj-commons/pomegranate
        
       ___________________________________________________________________
       (page generated 2021-07-01 23:01 UTC)