1. 7

    I think the “liveness” aspect when building UIs (and more!) is generally really not appreciated enough. Hot-reload can be very helpful but as pointed out in the post, interacting with the UI and supporting data structures through a repl / evaluate interface makes things even much more concrete.. and fun! From my point of view and experience this enables not only much faster iteration but also allows to ask “what if” questions that might not even come to mind in more static frameworks. So I’m really excited about this project.

    1. 6

      I agree with the sentiment that rewrite-it-in-X is not viable for many software projects, though the reasoning of why Zig and not Rust and can’t fully agree with. Rust support for interfacing C is really good and for C++ there is https://cxx.rs emerging. Don’t get me wrong, I like Zig and want it to succeed, just that for that specific purpose Rust might be the better target because of the guaranteed absence of undefined behavior of safe Rust code. See e.g. https://daniel.haxx.se/blog/2020/10/09/rust-in-curl-with-hyper/ of how curl allows to be configured with certain Rust-based components.

      1. 15

        The post is almost in its entirety about using Zig to compile (zig cc) and build (zig build) C/C++ projects. This is not something that Rust intends to offer and has very little to do with interfacing with C. It’s about being a C/C++ compiler.

        The only part where I mention extending a C project with Zig I also mention that you can do the same with Rust.

        1. 8

          Rust has a weaker build.rs and cc crate, but even this is often sufficient to throw away the C build system and Ship-Of-Theseus it in Rust.

          1. 2

            Indeed, my comment was tangential and in regards to

            Instead of running away from the C/C++ ecosystem, we must find a way of moving forward that doesn’t start by throwing in the trash everything that we have built in the last 40 years.

            with which I very much agree. If you talk about this then of course the question of C/C++ interop comes up. From my own cursory attempts of making zig talk to C libs I ran into problems pretty quickly trying to consume Win32 APIs as the header files could not be parsed. This of course can be worked around and fixed but it is pretty central to the question. (And Rust’s cbindgen has it limits as well of course). My understanding is also that zig provides no direct way to call into c++, which is undoubtedly a tricky subject but also this is quite central to the topic.

            But back to the build system: My very incomplete understanding of the example of implementing a redis command that you provide is that this also required reworking some of the Makefiles. This is expected of course but I doubt a little that you can just throw in zig into any sized decades old codebase without issues. It might still be easier than to integrate with Rust/cargo but there is work there either way. And so far having a custom build.rs that uses the cc and/or cmake crate has provided good build support for the things I attempted.

            And don’t get me wrong, I like zig and the approach it takes. The compile time meta programming is really cool and the focus on a fast and versatile toolchain is great. I definitely want it to succeed and to offer a real “systems programming” alternative.

        1. 8

          As someone whose written Lisp, Ruby, and Node on one side, and Go and Haskell on the other, I’ve always been curious about this apparent split between strongly typed languages and languages with really good interactive connect-to-production drop-into-a-useful-debugger-on-crash REPLs. Is this intrinsic to the language, or just a result of differing focuses on the tooling?

          I’m currently leaning towards “this is just a side effect of tooling focuses”, although I have yet to see even a really compelling UX sketch for a debug-it-in-prod REPL for a typed language. (For example, what would it mean to rebind a name to a value of a different type while paused computations may still refer to that name after resuming?)

          1. 5

            It depends what you mean by “debug”. If you want to inspect values, execute functions, modify value of same type - that’s supported by a lot of languages. GDB will let you do all three.

            But the idea of using a value of a different type is… interesting. I don’t see why would you ever want to do that in production. You can of course replace an object with a vtable-compatible one. (Or however your language does dispatch) But a completely different type? Technically: You’d need to also replace all the following functions to match the change and all places which produce that value. Practically: Why not take a core dump and debug this outside of production instead?

            (An incompatible type also doesn’t make sense really - the code won’t do the right thing with it in about the same way a dynamically typed language wouldn’t)

            And finally as a devopsy person I’d say: “if you feel the need to drop into a debugger in production that means we should improve tracing / logging / automated coredump extraction instead.”

            1. 2

              I’ve always been curious about this apparent split between strongly typed languages and languages with really good interactive connect-to-production drop-into-a-useful-debugger-on-crash REPLs

              This split is indeed interesting and it seems deep and hard to overcome. Languages emphasizing static typing provide a certain level of confidence, if it typechecks it is likely correct. But this requires an enforcement of type constraints across the program otherwise that promise is broken.

              Interactive languages are more pragmatic. In Smalltalk, modifying a method in the debugger at runtime will compile and install it in the class, but existing instances of that method on the callstack will refer to the old one until they are unwound. There basically is no distinction in runtime / compile time which in practice works very well but is not really compatible with the confidence guarantees of more statically typed approaches.

              I always wondered what a more nuanced compile time / runtime model would look like, basically introducing the notion of first-class behavior updates like Erlang. In different generations of code different type constraints apply. Which in turn means that for soundness the generations wouldn’t really be able to interact.

            1. 12

              I’m confused by the distinction between Emacs and an IDE. In what way is Emacs not an IDE? It’s more of an integrated environment than any other IDE I have used, IntelliJ included. Hell in Emacs I can read my emails and browse the web.

              1. 19

                That’s a sad terminology confusion: when people say IDE today, they often don’t have the original “integrated” meaning. Rather, they mean “that, which understands the exact semantics of the code and provides semantic-driven code assistance”. PostIntelliJ is the canonical post which captures the essence of this new IDE meaning. As I work on IDEs (in the modern sense), I tried to invent a better term here (code understander) but it doesn’t stick even with me. So I resorted to just re-defining IDE as Intelligent Development Environment.

                While Emacs is indeed probably the most integrated of them all, it doesn’t fare too well on the intelligent bits. There were attempts to add semantic understanding to emacs: CEDET had some grand vision, js2-mode a real parser for JavaScript, but they are nowhere close to the level of semantic support you get in IntelliJ.

                Today, there’s a trend for separating the semantic engine from the editor front-end (LSP), so we might see Emacs as intelligent environment in the future. Qualitatively, I think that Emacs + lsp-mode for a language which has an OK LSP implementation passes as IDE. Quantitatively, today LSP capabilities are inferior to those of IntelliJ.

                1. 2

                  Totally agree that our development tooling still has a lot of areas to improve, “intelligent bits” as you call it but also better support in terms of debugging, tracing, and insights into a programs runtime. The existing stepping debugger support in emacs & most other tools are just very basic and haven’t really changed on what existed 25, 30 years ago. A “live development” experience such as with the Smalltalk debugger is still missing in most other tooling (except for Common Lisp / Scheme to some degree).

                  In any case, thank you & the rust-analyzer team in particular that Emacs + rust-analyzer is my favorite Rust development environment that is getting better every week!

                2. 2

                  I’ve used Vim since the early 2000s. Each tool has a purpose. In Intellij IDEA for example, you can create a kotlin based spring boot project and start debugging or start writinh some test cases and run them to see the coverage. It will underline code where you need to add null checks. It will auto-install dependencies in your pom or gradle files. This is supported out of the box as expected from any IDE. It is a time saver. I install emacs and stare at a blank page. No “create project” menus, no “project options” it just stares with its gray, listless, lifeless terminal screen.

                  1. 1

                    Good point. “Specialized IDE” would have been more accurate but would have been more verbose

                  1. 4

                    One function I found useful to make smooth but snappy slider UIs and such is “detent”

                    function detent(n, detent, grid, snap) {
                      // This function is useful to implement smooth transitions and snapping.
                      // Map all values that are within detent/2 of any multiple of grid to
                      // that multiple. Otherwise, if snap is true, return self, meaning that
                      // the values in the dead zone will never be returned. If snap is
                      // false, then expand the range between dead zone so that it covers the
                      // range between multiples of the grid, and scale the value by that
                      // factor.
                      var r1 = roundTo(n, grid); // Nearest multiple of grid
                      if (Math.abs(n - r1) < detent / 2) return r1; // Snap to that multiple...
                      if (snap) return n // ...and return n
                      // or compute nearest end of dead zone
                      var r2 = n < r1 ? r1 - (detent / 2) : r1 + (detent / 2);
                      // and scale values between dead zones to fill range between multiples
                      return r1 + ((n - r2) * grid / (grid - detent));
                    }
                    

                    (that’s from lively.lang, MIT licensed)

                    Here is an example of a free-form rotation that still allows stickiness at 45 degree angles: https://twitter.com/robertkrahn/status/1403102487717023746 The gif doesn’t really transport it but it just feels “smooth”.

                    I’ve first seen it in Squeak/Smalltalk where it was added by Dan Ingalls in 1998 but don’t know what the history behind it is. It might have been part of earlier Smalltalks. For completeness, here is the Smalltalk version:

                    detentBy: detent atMultiplesOf: grid snap: snap
                       "Map all values that are within detent/2 of any multiple of grid to that multiple.  Otherwise, if snap is true, return self, meaning that the values in the dead zone will never be returned.  If snap is false, then expand the range between dead zones so that it covers the range between multiples of the grid, and scale the value by that factor."
                       | r1 r2 |
                       r1 := self roundTo: grid.  "Nearest multiple of grid"
                       (self roundTo: detent) = r1 ifTrue: [^ r1].  "Snap to that multiple..."
                       snap ifTrue: [^ self].  "...or return self"
                    
                       r2 := self < r1  "Nearest end of dead zone"
                           ifTrue: [r1 - (detent asFloat/2)]
                           ifFalse: [r1 + (detent asFloat/2)].
                       "Scale values between dead zones to fill range between multiples"
                       ^ r1 + ((self - r2) * grid asFloat / (grid - detent))
                    

                    (that version is from Squeak 5.3, MIT licensed with the changeset indicating di 2/19/98 21:58 as the author).

                    1. 1

                      Could you provide some more information about detent? I have tried to find something, but without any luck

                      1. 4

                        In essence it’s just a way to provide a free-form choice of values in a certain range while also allow snapping to certain multiples /without/ disallowing certain values (e.g. those that are close to multiples).

                        Here are two plots:

                        (and sorry for the links to twitter but I dunno how to share images otherwise)

                    1. 1

                      livereload is a useful set of tooling even when you are not building a webpage.

                      entr is a good tool for that purpose: http://eradman.com/entrproject/

                      1. 1

                        Oooh, I do like the simplicity of the interface here, and I think I will be using this when I have some ad-hoc test reload loop to work with.

                      1. 12

                        I think I get what you’re saying here, but I’d say you’re mixing up mechanisms and goals.

                        The goal is “safe concurrent programming”. Lack of race conditions, etc. There are (at least) two different mechanisms for that:

                        • immutability, as in functional programming languages (Clojure, Haskell, etc.)
                        • ownership, as in Rust

                        Just because Rust has the same goal as a functional programming language, but uses a different mechanism, doesn’t make it functional!


                        This is basically the viewpoint here, and I talked with another Rust programmer on HN who agrees: Rust is procedural, but it’s safe with respect to concurrency. (It’s also memory safe, but so are Java and Python, which are also procedural in the sense that idiomatic code often relies on mutation.)

                        https://boats.gitlab.io/blog/post/notes-on-a-smaller-rust/

                        As I said once, pure functional programming is an ingenious trick to show you can code without mutation, but Rust is an even cleverer trick to show you can just have mutation.

                        1. 4

                          100% agree!

                          Rust solves the same problem(s) that functional programming solves, but in a different (better, more performant) way.

                          I think it’s weird that people mention immutability as an example that makes Rust “functional”. It’s the exact opposite! Rust has “controlled mutation”. It’s perfectly safe (and often preferred) to pass by mutable reference in Rust. That’s expressly NOT functional because any function that takes a mut ref is not pure.

                          It’s the borrow checker that PRECLUDES (idiomatic) Rust from being functional, IMO.

                          1. 1

                            Yes, exactly, and this is where I see similarities between Rust and Clojure. Clojure (also not a pure FP language but leaning very heavily on immutability) provides different means to control change (atoms, transactions, agents, channels), which are mostly different from the change control the Rust offers (cells are sort of atoms but the borrow checker is of course in a different category). Even though those mechanisms are different, they can influence the design of programs in sort of similar ways.

                            1. 1

                              I did a small project in Clojure a few years ago (while spec was just about to be released as an experimental feature), so my memory of the language might be a little fuzzy.

                              What about Clojure makes you feel like it’s not a pure FP language? It doesn’t technically prevent side-effects, but does any language besides Haskell actually do that?

                              I felt like all other aspects of it “count” as functional. In particular, I don’t remember being able to mutate a function’s input parameters through its reference. I could be mistaken though, because I probably didn’t want to do that… :D

                              1. 2

                                Can’t speak for them, but when people call a language “purely functional” they often mean pure as in purity - the specific property of the language not permitting side effects, randomness, or mutation. Not as in “all-in on being functional” or “very functional”

                                1. 1

                                  That’s fair, but then why do they spell “Haskell” that way? :P

                                2. 1

                                  As became apparent with many of the replies here, the definition for whats a functional PL can somewhat differ :) The list of the wikipedia article https://en.wikipedia.org/wiki/Functional_programming is fairly inclusive and contains Clojure. In practice, idiomatic Clojure is very much focused on pure, immutable functions. At the same time it defines itself as a host language - running on top of Java / JavaScript C# (once upon a time). It allows to directly call stateful methods using the interop features and especially in ClojureScript I have seen pretty stateful programs. For that reason and for the audience here, the qualification.

                            2. 4

                              Thank you for your reply! Now Rust functions that are not requiring exclusive access to any parameter and don’t use cells or unsafe are referentially transparent. Of course that is not unique to Rust. If you stick enough consts in weird places into a C++ function you can achieve similar things. But the Rust compiler “rewards” that by requiring less strict conditions on parameter / context when calling such a function. That is one part I wanted to convey.

                              But I understand and fully agree with you that Rust provides a language mechanism that is different than what FP languages offer and which has different trade-offs. Thank you in particular for the link. I’ll update the post tomorrow and will link / quote your comment as this adds an important aspect I did not consider.

                              1. 2

                                I’ve had this realization myself, and so I made a little chart to help convey it: http://paste.debian.net/1183860/

                                I hope people find it useful.

                              1. 39

                                And then this is my claim: The ownership model, standard library, emphasize on iterators, and numerous other things do lead to code that is usually most idiomatic when written in a functional style. Thus, the positive properties listed above that are associated with functional programming and immutability are present in Rust programs, and, to a degree, even statically enforced by the compiler. For me, that is a joy!

                                I guess I don’t really agree with all of this. I agree that Rust gets the nice properties of functional languages at least when it comes to mutation, but if someone read the body of Rust code I have written and came back and said, “it really resembles the code I typically write on Lisp/ML/Haskell,” then I would be really surprised. I rarely write higher order functions. I rarely write recursive functions or folds. I rarely use persistent data structures and instead use mutation without reservation.

                                I think that if I were forced to choose, I would just say that Rust is a procedural language. But of course, that leaves so much out. Such is the inherent problem with categorization in the first place.

                                1. 11

                                  I agree with your sentiment. When I started Rust, I was using OCaml as my main programming language and I tried to emulate the OCaml style of functions taking immutable values in and returning immutable values and I quickly found myself struggling and talked about it on Reddit. I found that, although Rust does have functional programming genetic material (being bootstrapped in OCaml, GC from way-back-when that lent itself better to FP, current closure-strong collection APIs, etc.), Rust is best used as a procedural language. Trying to go against that is like writing imperative code in a functional language: it can be done, but the resulting software will be uglier, slower, and less maintainable for contributors.

                                  I rarely use persistent data structures and instead use mutation without reservation.

                                  Functional languages take away mutability from the “mutability + aliasing = bugs” equation; Rust takes away the aliasing — or at least, the uncontrolled aliasing. To me, it then makes sense to feel comfortable using mutability (and the advantages it offers), safe in the knowledge that the compiler is helping you avoid bugs.

                                  1. 4

                                    Functional languages take away mutability from the “mutability + aliasing = bugs” equation; Rust takes away the aliasing — or at least, the uncontrolled aliasing. To me, it then makes sense to feel comfortable using mutability (and the advantages it offers), safe in the knowledge that the compiler is helping you avoid bugs.

                                    Yes, that’s what I meant with, “I agree that Rust gets the nice properties of functional languages at least when it comes to mutation.” The OP also makes this point. My point here was to contrast typical Rust code with typical code written in a functional programming language. That is different from contrasting the goals and problems solved by the respective paradigms.

                                    I think we’re in agreement FWIW. Just seemed like a small misunderstanding here worth correcting.

                                    1. 2

                                      Well said! You should go with the grain of the language. Rust encourages mutation instead of superfluous copies. The borrow checker stops you from mutating in ways that are likely to cause bugs, so there’s no point in making copies when you don’t need to.

                                      FP is going to be like OOP in a few years. Right now everyone is trying to “FP all the things”. Monads in JavaScript? Are you kdding me? Stahp.

                                      I remember trying to OOP-ize everything I came across. I tried to write OO Python: a bunch of classes with “private” (double underscore) fields and side-effects, etc. It worked about as well as you’d expect.

                                    2. 4

                                      I understand your point and I tried to make clear that Rust code will unlikely equal a Haskell solution for the same problem. Not in terms of control structures and maybe not even from the overall design (Haskell maybe “world loop” deriving new state, Rust maybe event loop modifying application state directly which is probably not held in one single place).

                                      That said, from approaching program design in Clojure, I tend to create data structures similarly. Favoring acyclic graphs and being picky with where I change it. Rather to have some unidirectional control flow (thinking about UIs now, e.g. yew.rs, tui.rs or even gtk). In other “imperative” languages (like C++) I would approach it differently.

                                      As of functions: I love the versatility of iterators. Mapping or folding a sequence of results into a Result and not having to resort to for loops is nice. But I agree with higher order functions being used rarely.

                                      As you point out, pressing a rubber stamp on things has issues. This was meant more to provoke some thought and discussion ;)

                                    1. 18

                                      I’m increasingly coming around to the conclusion that there’s no such thing as a functional programming language; only programs that are written in a more or less functional style. A language can offer features that encourage or discourage the writing of functional programs, and when people say “language X is a functional language” what they mean is that it encourages the writing of functional programs.

                                      That said, any language with statements and no repl is difficult for me to describe as functional.

                                      1. 7

                                        I’ve joked before, but it’s somewhat true, that there’s really a hierarchy of “functional” which depends entirely on which other languages you sneer at for being insufficiently “functional” and which you sneer at for having uselessly gone too far in pursuit of purity.

                                        Like, the base level is you join the Church, you renounce von Neumann and all his works. There’s a level where you sneer at any poor pleb whose language doesn’t guarantee tail-call optimization. There’s a level where you sneer at any poor pleb whose language is based on untyped lambda calculus. There’s a level where you sneer at any poor pleb whose language doesn’t support fully monoiconic dyads bound over the closed field of a homomorphic Bourbaki trinoid. And at each level you also sneer at the people “above” you for going overboard and forgetting that programming needs to be practical, too.

                                        1. 4

                                          This is how I teach paradigms at the start of my Rust course! Programming paradigms are about a mental model for computation (“do I think of this in terms of functions / objects / logical relations / blocks / etc.”), and language support for a paradigm is about how well you can translate that mental model into code in that language. People sometimes don’t like this definition because it’s subjective, but it avoids the problems with defining paradigms extensionally or intensionally.

                                          If you define them extensionally, you’ll find that no one can agree on what the extent is. “Hey you left X language out of the functional list!” “Yes it isn’t functional” “But it is!” and so on.

                                          If you define them intensionally, you’ll find that no one can agree on what features constitute the necessary and sufficient conditions for inclusion in the set. “Functional programming requires automatic currying!” “No it doesn’t, but it does require partial function application!” “You’re both wrong, but it does require a powerful type system!” “What does ‘powerful’ mean?” and so on.

                                          So instead you say “well when I think of my program in terms of functions, I find that I can write the code that matches my mental model really easily in X language, so I say it’s functional for me!”

                                          Honestly, part of why I like this is that I think it helps us get away from an endless and unsolvable definitional fight and into the more interesting questions of how features intersect to increase of decrease comfort with common mental models.

                                          1. 2

                                            Honestly, part of why I like this is that I think it helps us get away from an endless and unsolvable definitional fight and into the more interesting questions of how features intersect to increase of decrease comfort with common mental models.

                                            I love how the other comments in this thread back this up. People are arguing over “no, a functional language must have X” / “no, it means Y” and they’re never going to agree with each other. Just acknowledge that fact and move on!

                                          2. 3

                                            This is absolutely true, IMO. And the same can be said with OOP.

                                            A “functional language” would really be any language that strongly encourages a functional style or truly forbids an OOP style. I think Haskell and Elixir are pretty close to forbidding OOP programs.

                                            Likewise, an OOP language is one that strongly encourages an object oriented style. JavaScript is OO because even functions are objects and you can add behaviors and properties to any object.

                                            Etc, etc.

                                            But I’m a bit confused by your comment about statements. Rust is pretty expression oriented. if is an expression, match is an expression, loops are expressions that return the value of its final iteration, all functions return the final expression in its body as the return value, etc.

                                            1. 2

                                              I think Haskell and Elixir are pretty close to forbidding OOP programs.

                                              In what way do you see that? I guess it depends what you mean by “OOP” of course but Haskell has several powerful ways to do OOP-style message passing and encapsulation.

                                              1. 1

                                                I’m sorry for the confusion. Everyone means something different when they say “OOP” (and “FP”). When I said OOP, I meant a style that revolves around “objects”. In my mind an object is something that has hidden, mutable, state. A black box, if you will. You may call a method on an object (or send it message), but you are not necessarily guaranteed the same response every time (think sending an HTTP request, a RNG, even a mutable Map/Dictionary is an object per my defiinition).

                                                I’ve never used Haskell for serious work, so I could’ve been totally off base there. And, actually, I guess Elixir does have message passing between processes. I was only thinking of the code inside a process… So, I’m probably wrong on both counts!

                                                1. 1

                                                  Just as an example, here’s one way to do mutable student message passing style in Haskell:

                                                  https://paste.sr.ht/~singpolyma/c618d894e7493d7197ef745035a8691d53e2a193

                                                  (This is an example and a real system would have to handle a few cases this does not.) In this case it’s a monitor (threaded and threadsafe) and you can’t do inheritance (but you can do composition).

                                              2. 1

                                                A “functional language” would really be any language that strongly encourages a functional style or truly forbids an OOP style.

                                                Gonna have to disagree here; whether something encourages object oriented programs or functional programs should be thought of as two orthogonal concerns that simply happen to be correlated in most widely-used languages. That said, “OOP” is such a poorly-defined term that I’m not even sure it’s worth spending any effort untangling this; IMO the term should be completely abandoned and more specific terms should be used in its place, like “message-passing”, “inheritance”, “encapsulation”, etc. (For instance, Elixir has great support for message passing and encapsulation, two cornerstones of what is often called “OOP”. No inheritance, but that’s great because inheritance is a mistake.)

                                                But I’m a bit confused by your comment about statements.

                                                I looked into it and … it IS confusing! Rust says that they “have statements” but what they really have is expressions that return Unit. Calling that a statement is pretty misleading IMO, because every other language I know that “has statements” means something completely different by it.

                                                1. 2

                                                  That said, “OOP” is such a poorly-defined term that I’m not even sure it’s worth spending any effort untangling this; IMO the term should be completely abandoned and more specific terms should be used in its place, like “message-passing”, “inheritance”, “encapsulation”, etc. (For instance, Elixir has great support for message passing and encapsulation, two cornerstones of what is often called “OOP”. No inheritance, but that’s great because inheritance is a mistake.)

                                                  Yeah, it’s definitely poorly defined. And my examples of Haskell and Elixir were actually bad examples. When I think of OOP, I’m thinking about black boxes of (potentially) mutable state and “message passing” (which may just be calling methods). You can’t, in theory, expect to get the same “response” if you send multiple messages to an object.

                                                  As you said, Elixir is a good example of both FP and OOP. Kind of OOP in the large, FP in the small.

                                                  Apologies for the confusion.

                                                  I looked into it and … it IS confusing! Rust says that they “have statements” but what they really have is expressions that return Unit. Calling that a statement is pretty misleading IMO, because every other language I know that “has statements” means something completely different by it.

                                                  Yeah, that’s strange that Rust docs would say they have statements… Maybe assignment is a statement? I don’t know. Most stuff in Rust is expressions, though- even the for loop that always returns Unit/void/whatever. It’s a neat language. Definitely not (pure)-function-oriented, IMO, but really fun, ergonomic, and safe for a systems language.

                                                  I think the Rust docs also used to say that it’s not OOP. I think that’s wrong, too. It doesn’t have struct/class inheritance, but I think that you can get really far with an OOP style in Rust- struct fields are private by default; mutation is controlled via the borrow checker; traits are like type classes and if you write “object-safe” traits, you can pass trait objects around.

                                              3. 3

                                                For the repl: Give https://github.com/google/evcxr/blob/master/evcxr_repl a chance. I just became aware of that project recently (the fact that they have a standalone repl) and have not yet tried to push it. It’s certainly appears not that full featured compared to dynamic languages or ghci but it should be good enough to make an inferior lisp mode out of it.

                                                1. 11

                                                  Thanks but if the language doesn’t have a repl built-in it’s a good sign that it’s creators don’t value any of the same things I value in a language, so I don’t think rust would be a good fit for me.

                                                  1. 3

                                                    Never change, Technomancy ❤️

                                                2. 1

                                                  I’m increasingly coming around to the conclusion there’s no such thing as a functional programming language

                                                  The way I see it, a functional programming language is a one that maintains referential transparency.

                                                  Lambda calculus, Haskell, Elm, Purescript, Futhark, Agda, Coq, and Idris are some examples.

                                                  Then, languages which don’t enforce referential transparency fall on the scale of “more” or “less” functional, based on how easy it is to write pure code, and how frequently it is done in practice.

                                                1. 4

                                                  For more historical background and details on the topic, the two HOPL papers are recommended:

                                                  1. 5

                                                    I love emacs and org mode, use it every day for note taking and time tracking. I found the options for reviewing activities with agenda and clocktables and such not so great, though. I made https://github.com/rksm/clj-org-analyzer a while ago to address this. It’s sort of your own github tiles with the ability to drill in.

                                                    1. 2

                                                      This solution isn’t quite ready yet but we are targeting the exact use case of sharing application windows, cross-platform and with remote-control capabilities: https://www.coscreen.co. If you are interested, please consider subscribing, we are currently working on acquiring funding and plan on opening up our beta test later this year.

                                                      1. 19

                                                        So why is CL not more popular ?

                                                        Because the multiplicity of implementations means that the package you need might not be available for your Lisp and some fundamental feature you need simply doesn’t exist.

                                                        Because the standard library is enormous but misses a lot of what the modern user needs. There’s no standard way to do networking in CL, for example. Sure there are third-party and implementation-specific libraries (and good ones) but they’re not just “there”. This only gets worse as the use-case gets more specific: let’s say I want to talk to an IMAP server, retrieve emails, parse them, and then open up a connection to an SMTP server. Python has literally every step of that in the standard library, and numerous third-party implementations of each that all work on the reference implementation of Python (CPython). CL doesn’t.

                                                        Because of the mixed metaphors. CLOS is there, and fantastic (better than anything that came after), but there’s also non-CLOS parts of the standard…and some libraries are OO and some aren’t and some are halfway and so on.

                                                        If you’re writing a truly unprecedented piece of code, something that no library already exists for, then CL is the better language, but the minute you want to do something with an existing system, you’re suddenly looking at a paucity of support that may or may not even work on your system.

                                                        (Note that the slightly angry tone of this comment is out of a…frustrated love. I wish CL had remained at the top, it is a wonderful language. It just didn’t, and I’m disappointed. Maybe things have improved some in recent years? I hope so.)

                                                        1. 10

                                                          As a big user of Common Lisp, I can tell you that most of those don’t end up being problems in real life.

                                                          One of the ideas behind CL is that the language is extensible and the user community can extend it as they see fit. Going with your example of networking, the standard won’t be updated to include it because it can be added by user library. When a library solves a problem well it’ll become popular, get added to QuickLisp, and then it’s available for everybody - no standard update required.

                                                          As you point out, Python’s huge standard library doesn’t offer much benefit over Common Lisp’s way of doing it. Python will often have a suitable library in the stdlib, but also third party libraries to do the same thing in different or better ways. Realistically people should check Pip for a better library before using the one in the standard library. It’s really no different than checking for a library in Quicklisp.

                                                          And then there’s a maintenance cost for implementations to keep alive obsolete libraries like CGI and optparse in Python.

                                                          As far as implementation specific code goes, it’s usually just not a problem. There are “standard but not officially standard” libraries that hide the implementation details in almost every case. trivial-sockets, for example, is the socket networking library that uses macros and inline functions to call into the implementation specific socket libraries.

                                                          1. 5

                                                            So I used CL a fair bit years ago, and Scheme was my preferred extension language for a long time.

                                                            (Ironically, though, I’ve never liked Emacs…)

                                                            The standard-but-not-standard stuff is, IMHO, a problem for people getting into the language….it may not be a problem for experienced users, but approaching the language from the outside you’re going to say “Common Lisp can’t do networking?!?” and then look at Rust/Python/Go and see that all the stuff they want is already there. They’re not going to know about QuickLisp or whatever.

                                                            (Yes, Python has pypi/pip, I know, but you can do an awful lot of “real world, modern” coding in Python before you have to resort to PyPI. Same with Go, etc.)

                                                            The other problem, and like I said this may have changed, is that not everything works for every implementation. I recall CLISP not supporting some sort of library that seemed fairly important, while SBCL did (or vice-versa). So that adds onto the problem of having to choose an implementation on top of choosing a language.

                                                            (Note that it’s not quite the same as C/C++. With those languages, you’re generally using operating-system-provided facilities so the compiler matters less, and the STL is also reasonably comprehensive on top of that.)

                                                            Again, all this is just IMHO.

                                                            (And I’m old enough to remember when you had to roll most of your own stuff regardless of what you were doing…good times, but I’m too old for that now.)

                                                            1. 6

                                                              By this metric C can’t do networking, yet it’s doing OK.

                                                              1. 1

                                                                C can’t do many things compared to Python, at least not easily or well, but can do others. Let’s try to compare apples to apples.

                                                                1. 4

                                                                  The point is not C vs Python, is that the claim that C can’t do networking is patently false. And C doesn’t have networking in their standard. Ergo not having networking as part of your standard is not an impediment to have good networking support.

                                                                  1. 2

                                                                    C is however more practically important language, and again doing fine without networking in the spec.

                                                                    We can’t compare CL to Python anyway as Python is not standardised.

                                                                2. 5

                                                                  The standard-but-not-standard stuff is, IMHO, a problem for people getting into the language….it may not be a problem for experienced users, but approaching the language from the outside you’re going to say “Common Lisp can’t do networking?!?” and then look at Rust/Python/Go and see that all the stuff they want is already there. They’re not going to know about QuickLisp or whatever.

                                                                  I don’t think that’s a realistic situation. If a new user is given documentation about the Rust/Python/Go libraries, they can just as easily be given documentation about Common Lisp’s libraries.

                                                                  1. 3

                                                                    The standard-but-not-standard stuff is, IMHO, a problem for people getting into the language….it may not be a problem for experienced users, but approaching the language from the outside you’re going to say “Common Lisp can’t do networking?!?” and then look at Rust/Python/Go

                                                                    I would expect new comers to read their implementation’s documentation not saying CL can’t do networking: http://sbcl.org/manual/#Networking That is what I did with Python and Ruby.

                                                                    1. 1

                                                                      The other problem, and like I said this may have changed, is that not everything works for every implementation. I recall CLISP not supporting some sort of library that seemed fairly important, while SBCL did (or vice-versa). So that adds onto the problem of having to choose an implementation on top of choosing a language.

                                                                      This is not a problem. Every modern resource will say: “just install SBCL now”, and nobody recommends CLISP today (although it’s still developped, that could change). De-facto standard libraries are well referenced.

                                                                      I encountered an actor library made for CCL (open source) and another one for LispWorks. That’s the only moment I saw a difference so far.

                                                                      If newcomers find the Cookbook, they’ll be fine :) https://lispcookbook.github.io/cl-cookbook/getting-started.html

                                                                      1. 2

                                                                        nobody recommends CLISP today

                                                                        That makes me sad. CLISP was where I got started with Lisp…

                                                                        1. 2

                                                                          CLISP was unmaintained for the last decade, although it has seen a resurgence in activity in the last few years. CLISP has a great CLI REPL, which is what newcomers will first try to use.

                                                                          1. 1

                                                                            Almost a thing in common. My experiments went from Lisp-in-a-box -> CLISP IIRC. Long time ago, though.

                                                                          2. 1

                                                                            Hey Vincent, nice post on the post and kudos for evangelizing CL while keeping a positive attitude. You rock!

                                                                            1. 1

                                                                              wooo, thanks :)

                                                                      2. 5

                                                                        I mostly agree with the observations you make about CL but disagree with most of the conclusions. The only observation I disagree with is

                                                                        Because the standard library is enormous but misses a lot of what the modern user needs.

                                                                        CL doesn’t have an enormous standard library, which is why, as you mention the standard doesn’t include a lot of the the modern user needs. But that is the job of implementations. SBCL comes with networking, threads, timers, a test framework, mailboxes, an FFI. Commercial implementations such as LispWorks come with even more libraries.

                                                                        The only thing that I constantly wish CL had as part of the standard is built-in sets.

                                                                        There’s no standard way to do networking in CL, for example. Sure there are third-party and implementation-specific libraries (and good ones) but they’re not just “there”

                                                                        That is a commonly raised objection, but it makes no sense to me. Do people program w/o using GCC built-ins? Why would I not consider the documented libraries of my implementation as being there? Recently I’ve writing a Gopher client, the protocol implementation is ~100 LoC on SBCL w/ no dependencies (using sb-bsd-sockets). I didn’t have to go shopping for libraries. Just download the RFC and start hacking!

                                                                        Because of the mixed metaphors

                                                                        It is true that CL has mixed metaphors, different parts of the language have different objectives. But that is a necessity for a general purpose language, especially in one that wants a uniform view of the work. Lisp is a jealous god after all0. Mixed metaphors are not pretty, but they are a thing that CL gets right.

                                                                        looking at a paucity of support

                                                                        If one wants support, Lispworks and Franz are always there, they provide you with a lot of the standard library you asked for.

                                                                        (Note that the slightly angry tone of this comment is out of a…frustrated love.

                                                                        Your comment doesn’t read as angry to me. And there is a lot to be frustrated wrt Lisp’s history. What frustrates me the most is that due to lack of resources and experience reports ‘we’ (Lispers) haven’t been able to move Lisp past CL. One can read Baker’s critique of the DIN Kernel Defintion to see how much CL could be improved for the application programmer (Spoiler for rust fans, it involves linear types). I wish we would have moved past CL. I wish the MOP would be fleshed out more. But alas, all I can do is wish (and program in Lisp of course, it is still a great language today)

                                                                        Quoting below the Jealous God reference because linking to google groups suck

                                                                        You make a good point about the importance of environments. That’s precisely the advantage of Lisp. You get a uniform picture of the world – everything is Lisp. You don’t have to know anything about machine language, assemblers, linkers, and all plethora of itty bitty ‘tools’ with <3-character names. You have the same language for writing macros, the same language for writing system code, the same language for writing scripts, the same language for writing editor extensions, the same language for interacting with windows, etc.

                                                                        Now some would consider this sameness a negative, and with most languages it would be. But with the power of Lisp as an ubiquitous extension language, I don’t have to memorize all sorts of ‘special-purpose languages’, each with different syntax, different naming conventions, different shadowing semantics, different versions of ifthenelse, dowhile, etc.

                                                                        Lisp is the ultimate ‘fractal’ language, because it is good at all levels, from microcode to shell scripts.

                                                                        1. 1

                                                                          “But alas, all I can do is wish (and program in Lisp of course, it is still a great language today)”

                                                                          Nah, it’s Lisp: you can implement the better features and paradigms as macros in CL. Then, get their benefits even while the purpose-built language is still an academic prototype. ;)

                                                                        2. 5

                                                                          I have used CL only for small personal projects but so far I can only agree with @jlarocco that working with QuickLisp has been a very good experience. Libraries are easy to install (at runtime, no reload!) and typically quite reliable. Not everything is well documented but the nature of CL makes it easy to browse around and dig in.

                                                                          Coming from Smalltalk I really started loving CL for its interactive workflow and expressiveness. And I was more than once surprised by the performance of code generated by SBCLs JIT. With all that I would really love to work on a CL project professionally.

                                                                          That said, I think similar to Smalltalk, the versatility of the language is the main inhibitor of success. http://www.winestockwebdesign.com/Essays/Lisp_Curse.html lays it out nicely - a programming language that is standardized, not too crazy, and easy to onboard people to probably fits the needs of larger and large projects better.

                                                                          1. 2

                                                                            Coming from Smalltalk I really started loving CL for its interactive workflow and expressiveness.

                                                                            I thought Smalltalk was the most interactive platform ever O_o

                                                                            With all that I would really love to work on a CL project professionally.

                                                                            thanks to my blogging and other activities I was contacted this year to work for 2 months on a website :) Now, if all goes well, I’ll have an opportunity for six months… so, do stuff, contribute and show it :)

                                                                          1. 1

                                                                            Hadn’t found that, sorry.

                                                                          1. 3

                                                                            Yeah, there were various events that basically nullified the considerable traction that Smalltalk had in the 80s and early 90s. And yet, after almost 50 years, Smalltalk still has features not found anywhere.

                                                                            The awesome development experience aside, just consider containers: One of the main pain points is how to debug them [1]. If getting a stack trace and showing some code alongside it is such an achievement, just consider how cool it is to just snapshot your image when a failure occurs and load that as a running thing, with all the context and behavior still live – while also allowing you to make code changes right while you have an interactive debugger pointing at your issue.

                                                                            [1] https://youtu.be/AdMqCUhvRz8?t=1758

                                                                            1. 4

                                                                              fork() followed by abort() isn’t the same as what you described, but it’s a neat trick for C programs to get a debuggable core file and keep running.

                                                                              1. 1

                                                                                Having the process call gcore will take longer but also not lose the contents of other threads’ registers?

                                                                                1. 1

                                                                                  Not a bad idea, though this relies on having gcore (gdb) installed.

                                                                            1. 20

                                                                              I like Rust, there are good use cases for it, but “Rust is the new JavaScript”, etc? Rust’s restrictions, which make it so different and useful also slow down development velocity and application design. Hyping it up to be something it isn’t doesn’t help anyone (except maybe the authors click count). Different requirements, different tools, different solutions. Want a good explanation of the topic (and a pro-Rust talk)? See Bryan Cantrill’s “Platform Values” https://youtu.be/2wZ1pCpJUIM?t=126

                                                                              1. 1

                                                                                Fair point! Allthough the headlines for these certain paragraphs are a bit catchy, they have a true point: Bitcoin is a decentralised, sort of self-governed structure. Rust is doing the same. JavaScript runs everywhere, Rust does it too.

                                                                              1. 5

                                                                                Interesting choice to use JS, especially because types actually help a great deal while refactoring. And the rise of TypeScript. On the other hand, the first really useful tool for automated refactoring was implemented in Smalltalk [1].

                                                                                Anyway, personally this book had a big influence on me while learning “how to build software” (vs. hacking something together). Along with [2,3,4,5,6].

                                                                                • [1] Roberts et al. A Refactoring Tool for Smalltalk. http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.91.9246&rep=rep1&type=pdf
                                                                                • [2] Gamma et al. Design Patterns: Elements of Reusable Object-Oriented Software
                                                                                • [3] Kerievsky. Refactoring to Patterns
                                                                                • [4] Evans. Domain-Driven Design: Tackling Complexity in the Heart of Software
                                                                                • [5] Meszaros. xUnit Test Patterns: Refactoring Test Code
                                                                                • [6] Beck. Test Driven Development: By Example
                                                                                1. 1

                                                                                  Did you spend a lot of quality time on the C2 wiki too?

                                                                                    1. 2

                                                                                      Since “Ken Thompson Hack” was mentioned, I’m throwing in my obligatory link about guy who discovered it, told him about it, and co-invented ways to solve it before Thompson published it. I list solutions that achieved some of maximum assurance he was hoping for. rain1, some others, and I collected a lot of other building blocks here, too, if anyone wants to assure or play with them.

                                                                                    2. 1

                                                                                      I loved C2. I think it got shut down a bit over spam or something. Then redone as a less efficient site. I found it close to when one or other of those happened. Can’t remember. I do remember reading all the arguments on stuff like LISP and programming practices. Fun times.