20 ms·
Why I still Lisp
- saladbarstalker 6y agoIt's a shame that so many Americans lack access to a speech pathologist.
- bstpierre 6y ago> What improves (and guarantees) software quality is rigorous testing. To deliver high quality software, there is no other solution. This is 100% false. You can’t inject quality into a bad design via testing. You can’t guarantee quality or correctness through testing. Testing is the second worst place to find errors. You get a MUCH higher roi by producing thoughtful written designs and getting them peer reviewed. And again with code reviews. (Peer review is best but self review can also be very effective.) I’m not saying don’t test — testing is valuable, especially because it forces you to design for testability, and a suite of automated tests is valuable when making changes later.
- tggreene 6y agoI agree that testing is not a panacea, but I also don't think it is completely divorced from design. I see testing and design as cooperative processes, I agree that design is almost always the highest yielding process in any non-trivial program, but I think of testing almost as a design phase that tries to invert the design to look for weaknesses.
- chestervonwinch 6y agoRight, spending all your effort on the one true design gets you in architect astronaut territory. Similarly for existing systems, conjuring up the grand rewrite in the sky (as Robert Martin calls it, I think?) is equally poor. Instead, recognize that design is important but will constantly evolve. Tests give the ability to incrementally redesign quickly and confidently.
- Zelphyr 6y agoI suspect what he’s referring to is the fact that so many people think that, because they have 100% code coverage in their tests, they have quality software. I’ve seen so many times a team claim that with pride when it’s brutally obvious to anyone who looks that the product is broken. They just don’t seem to be able to reconcile that problem when asked about it. What you test matters every bit as much as how much you test. Tests require thought and design as well. Too many developers don’t seem to understand that.
- bstpierre 6y agoThat's part of what I was talking about, but the original statement in the article was that rigorous testing guarantees software quality, and it also says "there is no other solution", and that is demonstrably false. One example: A telecom product that processed phone calls. I stepped into a role on the team building the call progression handling system, which was based on an ad hoc state machine and some inter-task communication that included a combination of mutexes and message queues. It was a mess. Our testers succeeded in churning out a lot of bug reports. Not a single one of those bug reports did anything to improve the quality of the system -- even if we're not being pedantic and saying that only the patches in response to the bug reports were what might have improved the quality. Because none of the patching we could do really made any net increase in quality. The only thing that improved system quality was stepping back and re-examining the design. We changed the existing undocumented design to get rid of deadlocks by just using one inter-task mechanism (just queues, no mutexes) and by formalizing the state machines so that none of them got stranded in an unexpected state. Once the design was formalized this way we found -- by inspection, not testing -- a number of fundamental bugs in the states and transitions. After fixing those, more system testing showed some more bugs, but we were able to assess these in the context of the design and fix them at the root cause rather than just hacking on patches. The design changes also enabled automated integration testing that wasn't possible with the old ball of mud. This helped save time in allowing the [mostly manual] system testers to focus on certain areas of the system. I've worked in other situations where lots of testing just resulted in lots of bug reports, which ended up mostly getting deferred and thus did nothing whatsoever to improve product quality. And I've worked on software teams that were in fact producing rather high quality software, but we were building the wrong thing. It's really hard to fix bad requirements through testing. I don't want to sound like I'm anti-testing, but I think it should be understood that testing isn't the source of quality, it's just the last phase of a process that hopefully has (non-test) mechanisms to ensure a quality product in the upstream process phases.
- noisy_boy 6y agoTesting tends to validate design from the point of testability. If it feels like the code is resisting testing, that usually ends up being an indicator of bad design (though not always vice versa).
- plafl 6y agoExactly. Testability requires modularity and reproducibility.
- karmakaze 6y agoThis is where we are now. We know how to make testable designs, since effort has been put towards it. What we lost is being able to distinguish good vs bad designs in other key aspects. One indicator I use is the number of test cases that are needed for covering. A poorly separated design will need to test many combinations having not isolated the factors.
- noisy_boy 6y agoI have noticed another indicator of good design is how easy it is to add specific fixes/features without having to think too much about how many places you'll need to make changes. Interesting point about no. of test cases. I tend to end up with several combinations because I prefer to focus on integration tests more for crud-type applications (a test configuration type/class comes handy in such scenarios).
- karmakaze 6y agoAs an example, if testing a 'send' function that takes a sender, content/media type, and recipients where recipients can be a single contact or a group, one shouldn't have to check that all combinations of content/media-type works with single or group recipients and other variable option variables. If decomposed in orthogonal ways K+L+N+M tests and perhaps a handful overall edge cases should do and not KxLxNxM if each variable dimension has K, L, N, M possibilities. If not cleanly separated in the implementation structure, the cross product of test cases are needed.
- koonsolo 6y agoI wonder how much experience you have in the real world. All of us developers had this thought at some point. I've seen terrible code with plenty of quick hacks on top of that, which was running very stable, because it was battle tested for years in production. What do you think will happen when you refactor such code to a better design? All juniors would think this is the best way to get a stable codebase. Reality is that you will introduce bugs by refactoring, not get rid of it. Writing code means writing bugs, no matter how pretty your code is. This reminds me of some quote that "Junior developers write 1 bug per 10 lines of code, experienced coders know they write 1 bug per 10 lines of code"
- brabel 6y agoYou might be one of those experienced developers who has gone through their entire career without ever meeting a well designed, beautifully architectured application, and therefore concluded, sensibly, that all real-world applications look like shit if you look inside, and you seem to have come to believe that this is the only possible way. I have a different experience. Code that is well designed makes mistakes look obvious and the logic feel natural. It's very hard to write code like that, but I've seen it and I've been able to write it myself, so when I see a bunch of complicated code that only tests can tell whether or not it works, I know that the problem is really that the best design simply has not been found yet. If I find code that looks like shit and I realize there's a better design for it that will make the code clear and natural, I won't think twice to refactor it ruthlessly if such code has been a source of problems and time wasted (and fixing difficult to read code is huge costly to morale as well, which when taken into consideration, increases the cost of technical debt by a lot).
- koonsolo 6y ago> a well designed, beautifully architectured application I've seen everything during my career, bad code with lots of bugs, bad code running stable. Nice code full of bugs, and nice stable code. The reason something was stable was never how the code looked, but how battle tested the product was. You will learn, don't worry. Plus, at a certain point, it's about tradoffs and compromises. I would love to see highly optimized code that is easy to read. > only tests can tell whether or not it works Tests are nice, but the real test will be when users start breaking your app. I'm curious, what kind of product are you writing?
- karlicoss 6y ago> Languages based on the λ-calculus make it really easy to “play back the code” in your head. This really depends on the expression.. I've seen behemoth expressions such that you have no choice but write down intermediate results, or start explicitly breaking it down into sub-bindings so you could step through the code. And yes, you can usually step through subexpressions in the debugger, but if you have to debug anyway, it hardly matters that you can 'play back' the code in your head. I also noticed there is a tendency in lispy languages to avoid introducing variable bindings just for the sake of naming the subexpression, because it often comes with a `(let ((name (subexpression))) rest-of-code)`, which also results in extra tabulation for the rest of the body. Compare with variable introduction in languages like python/js/c++/rust, etc., where such thing wouldn't cause touching the rest of the function/clause. I guess it's more of a 'functional language' artifact, e.g. in Haskell you'd probably have a similar issue. The simple rules don't remove the complexity, the evaluation state has to live somewhere. > I have never had a static type checker (regardless of how sophisticated it is) help me prevent anything more than an obvious error (which should be caught in testing anyway). Err, really? Never dereferenced a null pointer, or tried using a list instead of a set? Static types are testing, it doesn't replace it, but massively saves mechanical work on writing dozens of dumb tests. Having tests is very important for documentation, regressions, end-to-end/integartion testing, but every test for something stupid as 'throws on passing null' is just a time drain (unless you're launching rockets or something of course). Anyway, it's possible to have the best of both worlds by using gradual typing like mypy or JS flow -- you get all the runtime benefits (flexibility/ability to temporary violate invariants) and you gradually harden code which makes sense to type. I really wish Elisp (the lisp I'm mostly dealing with) had some sort of types, at least for simple things list 'associative list', 'string', 'function reference', 'nullable thing', this would massively save me time on catching bugs. It has runtime type checks for `defcustom` things, but never seen type checking anywhere else (except for occasional runtime asserts). > The biggest advantage of this form of syntax is a form of minimalism — you don’t need spurious syntactic constructs to convey concepts. > call these things macros, or syntactic extensions. In other words, you can extend the syntax of your language to introduce new abstractions. I find these two sentences a bit contradictory ;) Anyway, I personally like macros, if used sparingly it can really help. One great upside of s-expressions for me is that you can do some cool things like 'find and replace' for whole subexpressions (for monkey patching third party code, for example). I use `el-patch` [0] in my emacs config and `advice-patch` for surgically changing the default behaviors of some org-mode functions to compile my blog [1]. That said a similar sort of thing is possible, for example, in python with `patchy` [2], and perhaps many other languages? But I guess it's not as organic as in lisps, e.g. `advice-patch` implementation is less than 100 LOC, whereas in case of python you have to rely on existing heavy lifting done by `ast` module. As of simple syntax, it really gets in the way sometimes, e.g. I'm always annoyed by constant quoting in Elisp because the same type of brackets (only `()`) is used. In comparison, in Clojure it's much more readable with (), [], {} (and more). > Lisp is not an interpreted language. It is not slow A bit of nitpicking, but... which Lisp? :) Anyway, these days it's often meaningless to say 'slow' without having a specific workload in mind and having done benchmarking; hardware improvements make it very hard to reason about. > all implementations come with lots and lots of levers to tweak performance for most programs. In some cases the programs might need assistance from faster languages like C and C++ because they are closer to the hardware, but with faster hardware, even that difference is becoming irrelevant. Well, this is true of most languages. [0] https://github.com/raxod502/el-patch#el-patch https://github.com/raxod502/el-patch#el-patch [1] https://github.com/karlicoss/beepb00p/blob/a4fd7cb95e1705412d68c568ea13f2b0a831527f/exobrain/src/publish.el#L45-L74 https://github.com/karlicoss/beepb00p/blob/a4fd7cb95e1705412... [2] https://github.com/adamchainz/patchy#patchy https://github.com/adamchainz/patchy#patchy
- vector_spaces 6y agoWow. I thought I recognized that name - the author of this post interviewed me for a job several years ago when I was first learning how to code. It didn't work out as I was waaaaaaay too junior at the time but I'll always remember that interview fondly. My background was more in math and my interests were in PL theory, so he quizzed me on the lambda calculus, and turned me onto Essentials of Programming Languages by Friedman & Wand (and off the bad compiler books I had been reading previously), and pointed me towards material for further study in programming languages and lambda calculus. The interview was still fairly practically oriented despite those detours, and I learned a great deal that helped me to quickly find a solid technical role not long after. But more importantly, he and his colleagues who interviewed me were incredibly kind and respectful, and instilled this sense that both computer science and software engineering could be a lot of fun. :)
- Donckele 6y agoYou should apply again, the company the author works for is looking for people “Proficient in Java and C++ with strong object-oriented design skills”.
- apricot 6y agoA great read for a lazy Sunday morning. The author's tastes in programming languages agree with my own, and he's defending them in an eloquent way so it's an article I read drinking coffee and nodding my head. Also describing Miranda as "Ugly Haskell's beautiful mom", and invoking old Hindi movie censorship to belittle static type checking is just mmph. <Italian chef's kiss>
- jgwil2 6y ago> This feature, called tail call optimization (or TCO), has been around for a few decades and it is a sad commentary on the state of our programming languages that none of the modern languages support it. TCO is part of ECMAScript specification, although apparently only Safari supports it.[0] [0] https://2ality.com/2015/06/tail-call-optimization.html https://2ality.com/2015/06/tail-call-optimization.html
- bjoli 6y agoWhich, considering JavaScript is a language many compile to, sucks. I have said it many times: recursion is unbiased. Compiling a languages looping facility to something recursive is simple. Forcing one looping facility into the shape of another looping facility is the path to suffering.
- lmilcin 6y agoI think Lisp is going to live forever as a niche tool for people who "get" it. I use Clojure on a daily basis. Not necessarily because it is best Lisp, but rather because I am working with Java applications and being able to reuse the same Java code I have already developed is a huge boon to me. If I was able to choose, I would be using Common Lisp. The way I use it is to quickly develop adhoc tools and PoCs and more rarely small UIs in re-frame. Working with Lisp is a joy for me and I always get this feeling of frustration when I have to go back to Java, especially if I have to translate to Java what I just prototyped in Clojure. Unfortunately, where I work (financial systems for financial institutions), I have trouble finding enough people, mature enough to be able to even propose working projects in Clojure. Close in my org structure there is a set of Clojure projects where the company got badly burnt. The code is a mess and a huge headache for management. The guys who wrote it were either promoted (claiming outstanding work on the project but not wanting to maintain it) or left. Now new developers are barely able to change anything without blowing it up. This underlines the fact that Lisp projects can very easily end in spectacular disasters. You have to be really incompetent to write a typical Java service in a way where it is no longer possible to develop it at all, but in Clojure it is just enough to get couple of people that are intelligent enough to write macros but not experienced enough to understand the dangers of lack framework forcing the structure of your application.
- vslira 6y agoHave you looked at Nubank? Largest neobank in the world by users and valuation, it's a clojure shop to such an extent they literally bought Cognitect No affiliation, just a satisfied customer
- mbrodersen 6y agoThere are examples of successful companies using any programming language. It doesn’t prove anything about the quality of the language. The world still succcesfully runs on COBOL. That is not a good enough reason to pick COBOL for your next project.
- sidebits 6y ago
- jgwil2 6y ago> I have never had a static type checker (regardless of how sophisticated it is) help me prevent anything more than an obvious error (which should be caught in testing anyway). This is the static vs dynamic type debate in a nutshell. Personally I think Typescript hits a sweet spot of static typing by default with an escape hatch (via the `any` type) when you need it. I think more self-documenting code makes the tradeoff well worth it (with the added guarantee that the documentation is correct). And most importantly, I like getting feedback from the editor as I'm typing rather than having to write code then run tests, then write some more and perhaps modify the tests, etc.
- tazjin 6y agoOn the other hand, I've worked with Erlang systems where almost all issues we saw in production would have been prevented by a type-checker. There are, however, very few ergonomic type systems. At the moment I think Rust is, sadly, the only one.
- coldtea 6y agoWhat makes it ergonomic in your opinion?
- coldtea 6y ago>Personally I think Typescript hits a sweet spot of static typing by default with an escape hatch (via the `any` type) when you need it. That has been available in Obj-C, C# and others...
- mssundaram 6y agoEven better, use `unknown` when at all possible instead of `any` - it's like `any` but you need to assert/inspect it to unwrap anything from it.
- frou_dh 6y agoIn that debate, we quite often hear something along the lines of "I can't remember the last time a static typechecker actually helped me" and that makes me imagine a fish saying they can't remember the last time that water actually helped them. Maybe the ambient help is so constant that it no longer even registers in long-term memory.
- coldtea 6y ago>I have never had a static type checker (regardless of how sophisticated it is) help me prevent anything more than an obvious error (which should be caught in testing anyway). Obvious in retrospect is not the same as obvious. And such errors happen all the time, the same way without syntax checks typos happen all the time. And "caught in testing" is 2 extra steps removed from caught immediately by the syntax checker running as you type or save: writing the tests and running the tests (and being thorough/lucky enough that this part of the code is covered in a test). >But it is a stupid tool. It can only do so much. So, you now end up with artificial rules about how to satisfy this tool. And things that I know (and can justify or even formally prove for my use cases) are perfectly fine to do are suddenly not. What thing that violates a type check would be "perfectly fine to do"? Dynamic languages still have types (since you still need to pass around the right object with the right methods or fields to the call site, else it will raise an exception when you use a method/access a field/run a function on the wrong type). def square(value): return value*value If value is not a numeric type, square is not going to be happy. And that's the case with most (all?) dynamic code. What would be a counter-example that something not satisfying to a type checker would be "perfectly fine to do" in a dynamic language? (I do accept that the static typed language is better to have generic and/or algebraic types, no make lots of invariants easier to express without ceremony).
- tom_ 6y agoSquaring is usually value*value. (a minor nit that shouldn't detract from your point - since in a dynamic language, you'd have potential type errors in addition to this sort of thing...)
- coldtea 6y ago>Squaring is usually valuevalue.* This version is optimized for the value of 2! (thanks, checked).
- deleted 6y ago[deleted]
- tines 6y ago
- sn41 6y agoThe author of the article, Anurag Mendhekar, is one of the authors in the original paper on AspectJ [1], which introduced Aspect-Oriented Programming. The origins of AOP are in Smalltalk and the Meta-Object Protocol, I think. I really liked Aspect-Oriented Programming, despite its great issues with mutually interfering aspects etc. I wonder if it will make it as a big paradigm, but I hope it does. [1] https://www.cs.ubc.ca/~gregor/papers/kiczales-ECOOP1997-AOP.pdf https://www.cs.ubc.ca/~gregor/papers/kiczales-ECOOP1997-AOP....
- rottc0dd 6y agoI have used aspectj in somewhat unhealthy dose in my project. Mostly for reasons not even intended as an use to aspect. We have to work on giant configs reified as giant java objects. And I have was able to collect enough metadata about the code using aspects and keep them tied to the live objects in such a way that I was able to simulate creation, transformation and move of method calls. yes, a byte code parser and reflection might have achieved the same, but the library made it breezy. Maybe, java is not a hacker's language but ecosystem comes with hackers toolbox.
- spacemanmatt 6y ago> not a hacker's language but ecosystem comes with hackers toolbox I have always loved calling Java the mullet of programming languages for this reason -- a shallow investigation reveals a GC'd OO language meant for getting down to the business of some boring insurance or finance apps. Around the back, it's got enough dynamic features for a full party.
- jmkr 6y ago> That got me through a bachelor’s degree in Computer Science, but it always left me wanting for more expressiveness in my programs. I share this sentiment, and I think a lot of it also comes from Rich Hickey talking about it. As I relate to what this article expresses, I've noticed that people who write lisp type stuff talk about programming in a different way. In some sense there is an academic feel to it. In another sense there is a maker feel to it. I think Dan Friedman might have said something similar about static typing not helping too much, but saw dependent types as something interesting
- mark_l_watson 6y agoLisp development is niche, but has sufficient use so that languages like Common Lisp, Clojure, and Racket will hopefully be supported and enjoyed for a long time. The author hits on the sore point of available libraries to hit APIs and I would add access to deep learning infrastructure. I use the Hy language (hylang, has a Clojure style syntax) that is built on Python (you can get a copy of my Hy book for free, the minimum price is free https://leanpub.com/hy-lisp-python https://leanpub.com/hy-lisp-python), but I still like Common Lisp as my main driver. Anyway, I consider myself to be very fortunate to have about half of my career since 1982 use Lisp languages.
- zokier 6y ago> For example, your invariant might be something like I’m expecting here a monotonically increasing array of numbers with a mean value of such and such and a standard deviation of such and such. The best any static type checking will let you do is “array[float]”. Isn't this just plainly false? With dependent type system wouldn't you be able to encode all such invariants into the types? Curry-Howard isomorphism and so on?
- sn41 6y agoI think so. Using existential types, we can encode predicates in such a way that the type is inhabited (i.e. there is a value of that type) only if the predicate is true. I am learning Forth and Idris in my spare time these days. I have tried writing merge of two sorted lists in Idris, with the constraint that the merged list must have the length equal to the sum of the lengths of the input lists etc. - frankly, I much prefer Forth :D https://www.idris-lang.org/ https://www.idris-lang.org/
- deleted 6y ago[deleted]
- taeric 6y agoI suspect the problem is the types begin to get fairly heavy weight. It can also start requiring you to make many atomic operations so that your invariant holds across larger chunks of code. And, that often isn't easy or really necessary.
- Zababa 6y agoThis is possible to do without dependent types. You could have something like this (using TS syntax): function produceComplexArray(arr: array<number>): ComplexArray | null { ... } function consumeComplexArray(arr: ComplexArray) { ... } If the only place where you can produce a ComplexArray is produceComplexArray, and produceComplexArray ensures that the arrays it receives are "a monotonically increasing array of numbers with a mean value of such and such and a standard deviation of such and such", then all the functions after produceComplexArray can be assured that they have the right array. Therefore, even with a relatively simple type system, you can ensure this invariant. I think I got this idea from this article: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/ https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-va...
- HerrMonnezza 6y agoIn other words, static typing is pointless. It has, maybe, some documentary value, but it does not substitute documentation on other invariants. For example, your invariant might be something like I’m expecting here a monotonically increasing array of numbers with a mean value of such and such and a standard deviation of such and such. The best any static type checking will let you do is “array[float]”. The rest of your invariant must be expressed in words as a documentation of that function. I wonder if "design by contract" [1] tooling would help here? It seems to me one could express the stated invariants of the array as pre- or post-conditions on any function processing that array. [1]: https://wiki.c2.com/?DesignByContract https://wiki.c2.com/?DesignByContract
- deleted 6y ago[deleted]
- johnisgood 6y agoThat is funny, Ada/SPARK is not mentioned at all! How strange. https://en.wikibooks.org/wiki/Ada_Programming/Contract_Based_Programming https://en.wikibooks.org/wiki/Ada_Programming/Contract_Based... http://www.ada-auth.org/standards/12rat/html/Rat12-2-3.html http://www.ada-auth.org/standards/12rat/html/Rat12-2-3.html https://docs.adacore.com/spark2014-docs/html/ug/en/source/how_to_write_subprogram_contracts.html#writing-contracts-for-functional-correctness https://docs.adacore.com/spark2014-docs/html/ug/en/source/ho... https://blog.adacore.com/contracts-of-functions-in-spark-2014 https://blog.adacore.com/contracts-of-functions-in-spark-201... Plus, I do not believe that static typing is pointless. Even if it were ONLY for documentation, that would already make it pretty useful, in my opinion, like Erlang's type specifications, although there is a static analysis tool called dialyzer that identifies software discrepancies such as type errors and such. In any case, from AdaCore's website: > In statically typed languages, a type is mainly (but not only) a compile time construct. It is a construct to enforce invariants about the behavior of a program. Invariants are unchangeable properties that hold for all variables of a given type. Enforcing them ensures, for example, that variables of a data type never have invalid values. > A type is used to reason about the objects a program manipulates (an object is a variable or a constant). The aim is to classify objects by what you can accomplish with them (i.e., the operations that are permitted), and this way you can reason about the correctness of the objects' values. Just for the curious: > A nice feature of Ada is that you can define your own integer types, based on the requirements of your program (i.e., the range of values that makes sense). In fact, the definitional mechanism that Ada provides forms the semantic basis for the predefined integer types. There is no "magical" built-in type in that regard, which is unlike most languages, and arguably very elegant.
- dj_mc_merlin 6y ago> You’re always thinking about how information is acted upon, how it is transformed, and how it is produced. I have yet to find a foundational framework that captures this inherent intentionality (the ‘how’) that is better than the λ-calculus. Yes! Lambda calculus is my "native brain programming language" and I can't find a language the expresses it better than the Lisps. I never have to bend around the language, it feels as if I can always do exactly the method I wanted to solve something. Another big benefit is the simple syntax makes complex metaprogramming achievable. You just move the s-expressions and atoms around. Unnest and nest them. Like normal code. It makes sense.
- tines 6y ago> The best any static type checking will let you do is “array[float]” I know this guy is smart and all, and I love Lisp as much as the next guy, but this article really reads like it was written by someone who hasn't used Haskell.
- mikewarot 6y agoI've installed portacle, and ended up in a nest of errors I didn't understand. Can anyone here recommend a non-editor based version of lisp?
- smabie 6y agowhat is a editor based version of Lisp? fwiw, I really like Racket. It's definitely the most clean/elegant Lisp around.
- mikewarot 6y agoThank you! Racket gives me error messages, which I can learn to understand, and doesn't take me into Emacs like portacle does. The tutorial helps too... more thanks!
- kingdomcome50 6y ago> I have never had a static type checker (regardless of how sophisticated it is) help me prevent anything more than an obvious error (which should be caught in testing anyway). "I have never worked in a non-trivial system that changed over time." FTFY
- indymike 6y agoWhen I look at languages I use regularly (largely Emacs), Lisp is the one I'd love to try to build something large with. It feels quite powerful and the syntax is pretty easy to understand. It could just be my personal lack of experience with Lisp, but there seems to be little standard in naming conventions and code formatting. This may be where a lot of developers look at Lisp code and declare it an unmaintainable mess. Often I've seen multiple styles of formatting in the same file when I look at non-emacs production code.
- ywei3410 6y agoEmacs is a bit special; a significant number of practitioners are hobbyists in ELisp and not professional programmers (which is a great thing!). I don't judge the quality of C# code by looking at amateur Unity scripts for example. Depending on your flavour of Lisp, there are a /ton/ of naming conventions. Here's a good reference for Scheme/Racket [1], under the section "Names". [1] https://docs.racket-lang.org/style/Textual_Matters.html https://docs.racket-lang.org/style/Textual_Matters.html
- tromp 6y agoI find it odd that the author qualifies Haskell as ugly and its ancestor Miranda as very beautiful as the two seem pretty similar in syntax to me. I find beauty in both. I would only qualify Template Haskell as ugly.
- carapace 6y agoLisp is too complicated. For years I used to say, "I'm keeping a bank of brain cells free for learning Lisp one day." I knew it was important, I knew one day I would sit down and learn Lisp. Eventually, that day came. As it happened, I chose PG's book to start with, and (as is my wont) I began with the Table of Contents. That's as far as I got. Somehow, in the years leading up to that moment, I had acquired the necessary background information to be able to "get" Lisp just from reading that ToC. My mind was blown. Here was the perfect, the complete, the only needful language. And then I got mad. I got mad at you and me and all our peers. For wasting time. For wasting attention. For fucking around with syntax and bullshit for fwcking decades when we had Lisp. All the time and energy and money, burned like so much trash, because not Lisp. So did I start using Lisp? No, of course not. Python pays (paid) the bills. Sick world. - - - - Anyway, in the meantime, I discovered a language called Joy. I'm pretty sure it's the simplest useful language. It is a point-free form that's good for Categorical Programming. It's not based on Lambda Calculus so there are no variables, no binding environments. You can do algebra on it and it's easier than Squiggol ( https://en.wikipedia.org/wiki/Bird%E2%80%93Meertens_formalism https://en.wikipedia.org/wiki/Bird%E2%80%93Meertens_formalis... ). I was messing around with Make-a-Lisp the other day but I didn't get very far because, as I say, Lisp is too complicated (not to understand, to implement. It's a PITA.) Joy is simpler. The syntax is trivial, the grammar is trivial, the interpreter is simple, it's just a beautiful elegant formal system. To be clear, I'm not complaining about Lisp, I think Lisp is the best language (Except for Joy, which is even better, IMO.)
- csb6 6y ago> Somehow, in the years leading up to that moment, I had acquired the necessary background information to be able to "get" Lisp just from reading that ToC. Damn, you are a genius! As for myself, I can only gain this level of understanding by skimming the index (or maybe the appendices). ;)
- continuational 6y agoLots of articles like this out there about Lisp. To quote Linus Thorvalds: > Talk is cheap, show me the code If you think you can write better code in Lisp, well, show us some examples of that.
- yawaramin 6y agoOK, here's an example: https://cs.brown.edu/~sk/Publications/Papers/Published/fkt-teach-scope-mut/paper.pdf https://cs.brown.edu/~sk/Publications/Papers/Published/fkt-t... In this paper the authors describe how students actually grasped fundamental concepts better in Schema than in Java, despite being taught both throughout the course of the semester.
- username90 6y agoIf they wrote java with closures people would understand it as well. Instead they used more complicated examples for java which skews the results. Example if you translate their lisp example directly. Their original lisp: ( let ([ x 3]) ( let ([ f ( lambda (y ) (+ x y ))]) ( let ([ x 5]) (f 10)))) Java: { int x = 3; { Function<Integer,Integer> f = y -> y + x; { int x = 5; { return f.apply(10); }}}} They argued that Java is less concise so they couldn't have as many examples, but I feel that example gets the point across just as well and it has comparable number of characters.
- mbrodersen 6y agoAny examples of successful large scale production code in LISP? There are many examples of those in Java/C++/...
- yawaramin 6y agoWalmart's transaction processing system in Clojure. Every single sale in at least North America goes through that system.
- carapace 6y ago> Now wait a minute, I’m sure you’re thinking. I’ve never proved sh*t about my functions. I’m betting that you have. And that you do all the time. You’re always convincing yourself that your function is doing the right thing. Yours may not be a formal proof (which may be what leads to some bugs), but reasoning about code is something that software developers do all the time. They’re playing the code back in their head to see how it behaves. I sometimes try to point this out when arguing in favor of Formal Methods. You're already using informal Formal Methods anyway. Just write your reasoning down and make the computer double-check it. "Oh the expense!" I hear them cry. But where's the spreadsheet comparing that with the expense of the faulty reasoning? Where is the Manhattan Project to lower the cost of logic? And don't get me started on (what Guy Steele Jr. calls) Computer Science Meta-notation, which has no official standard, and isn't a runnable language. ( https://groups.csail.mit.edu/mac/users/gjs/6.945/readings/Steele-MIT-April-2017.pdf https://groups.csail.mit.edu/mac/users/gjs/6.945/readings/St... )
- smlckz 6y agoCurrently, lisp is not a single language, but a family of similiar languages that originate from the LISP. The languages' inherent flexibility gives you too much freedom of expression to handle, especially for those who come with experience of strict languages. The minimalism of syntax and the ability to create in-effect a (sub-)language of your own also adds to that expressivity. But we need guards to protect us from the freedom of too much expressivity, see for example defmacro vs. syntax-rules/syntax-case etc. From my perspective, I want a language which gives me freedom when I want that, protection (''guardrails'') when I want that too, both if possible at the same time. On invariants, what dependent typing and property testing are giving us now? How is the room of improvement on that matter?
- TedShiller 6y agoWhy I’m a bad programmer
- Blikkentrekker 6y ago> But it has now taken a new interpretation in the last couple of decades: Static typing is a form of compile-time error checking, so it will help you produce better quality code. It is as if static typing is a magical theorem prover that will verify some deep properties of your program. This is where I call bullsh*t.[sic] I have never had a static type checker (regardless of how sophisticated it is) help me prevent anything more than an obvious error (which should be caught in testing anyway). That's an anecdote; facts are that serious critical bugs that lead to privilege escalation and loss of data have existed in codebases that would surely have been caught by various static type systems. > In other words, static typing is pointless. It has, maybe, some documentary value, but it does not substitute documentation on other invariants. For example, your invariant might be something like I’m expecting here a monotonically increasing array of numbers with a mean value of such and such and a standard deviation of such and such. The best any static type checking will let you do is “array[float]”. The rest of your invariant must be expressed in words as a documentation of that function. So why subject yourself to the misery of “array[float]”? With dependent typing, it's actually quite possible to both define a type for “sorted list” and prove to the type checker that a sorting algorithm does sort it.
- didibus 6y ago> That's an anecdote; facts are that serious critical bugs that lead to privilege escalation and loss of data have existed in codebases that would surely have been caught by various static type systems. An anecdote at this point is at least better than a baseless claim as you're making. Also keep in mind the article is in the context of Lisp, so it would be static types on top of a garbage collected memory managed runtime with runtime type checks.
- kazinator 6y ago> That's an anecdote; facts are that serious critical bugs that lead to privilege escalation and loss of data have existed in codebases that would surely have been caught by various static type systems. Do you have any non-strawman examples of this (e.g. C security holes are strawman).
- 6y ago
- jtth 6y agoAnyone know of something like Rails for a Lisp?
- arendtio 6y ago> Call-by-value > Mostly Functional > Dynamically Typed Sounds a bit like POSIX shell. Yeah, I know that especially people doing functional programming do not want to be compared to shell scripters, but since I know how horrible shell scripting is, I wonder if there could be a future, where a proper, mostly functional language could be the successor of the very practical bash. I mean, if we aren't doing anything soon, JavaScript might take that place...
- pjmlp 6y agoPOSIX shells are not as powerful as Lisp REPLs and Smalltalk transcript windows.
- cjallen88 6y agoMaybe have a look at Closh [1], a clojure based shell [1] https://github.com/dundalek/closh https://github.com/dundalek/closh
- arendtio 6y agoI think my problem is less, that there are no alternatives you can setup yourself, but more, that there is no useful standard ;-) But having a list of good alternatives if probably good too.
- hota_mazi 6y ago> As a programmer, I carry around invariants (which is a fancy name for properties about things in my program) in my head all the time. The author has probably not worked on large code bases or even in teams. I often forget even my own code's purpose six months after having written it, let alone code written by teams of hundreds of people and millions of lines of code. Dynamically types will kill you in such (common) situations. > In other words, static typing is pointless. Yes, author is definitely very young. There are two types of programmers: programmers who know that static typing is the only sane way to write robust, scalable, maintainable code, and programmers who haven't been in this profession for long enough.
- didibus 6y ago> I started serious programming in my teens in BASIC on a ZX Spectrum+, although I had previously dabbled in (hand-) writing Fortran programs > I ended up studying Programming Languages at Indiana University with Dan Friedman (of The Little Lisper / The Little Schemer fame). It was my introduction to Scheme (and the world of Lisp.) I finally knew that I had found the perfect medium to express my programs in. And it has not changed in those last 25 years Author seems to have over 25 years of experience.
- LaGrange 6y ago> Author seems to have over 25 years of experience. That doesn't mean much. The author has been a researcher, and then moved into management, now working on a relatively simple e-commerce project with one of the most hilarious mission statements I've ever read. It's the kind of guy that makes me _shudder_ when he shows up in a workplace - because I already know I'm going to have to clean up after them. His dismissal of the documentation value of type systems really tells you how much experience he _actually_ has - my comeback to static typing, as is for nearly every programmer I know, specifically because of how reliable things like "look up the source/documentation/whatever" can be in statically typed code. Not everyone cares, and that's fine, but such a dismissal tells me he's very sheltered, and vastly overestimates the real complexity of his own projext. Also apparently he worked on AspectJ, which to me tells me everything I need to know, and also probably explains why he hates the JVM even more than I do.
- omar_kha 6y agoif lisp is so good, where all the libraries to create cool stuff?
- tjr 6y agoWhat libraries are missing?
- jedimastert 6y ago> next cool feature being dropped into Python or Scala, or whatever their flavor of the month is I might be off-base here, but I feel like Python is at leasta little mature enough to not really justify calling it "flavor of the month"
- kazinator 6y agoHi all, I'm preparing release 250 of the TXR Language, the bulk of which is a Lisp dialect called TXR Lisp. Release 250 adds compiler optimizations, like jump threading, some dead code elimination, and a few peephole reductions. This work is finally possible because I had put it on hold due to not wanting to write such code without a pattern matcher, which we now have. I'm fixing two bugs that were reported by a user, which will be nice in such a landmark release (250 releases, wow!). The new structural pattern matching sub-language in TXR Lisp (new since 247 or 248) will be improved. Bugs are fixed. The way the @(or ...) pattern operator works has been rewritten, so certain corner test cases now pass. It generates better code. There are will be some new features in the matcher too. The @[fun ...] flavor of the predicate operator now lets you capture not only the variable, as usual, but using an extra argument, the value of the predicate (which could be an extended Boolean with an interesting value). For instance, is "abc" in the hash table htab? If so capture it as x, and also capture the value as y: This is the TXR Lisp interactive listener of TXR 249. Quit with :quit or Ctrl-D on an empty line. Ctrl-X ? for cheatsheet. Upgrade to TXR Pro for a one-time fee of learning Lisp! 1> (let ((htab #H(() ("abc" "foo") ("xyz" "bar")))) (when-match @[htab x y] "abc" (list x y))) ("abc" "foo") Here, htab is just being used as a function; any function will work that can take the argument "abc"; if it returns non-nil, then the predicate has rung true, the pattern has matched, and x takes "abc", and y the returned value. By the way, to see quips like "Upgrade to TXR Pro for a one-time fee of learning Lisp!" you must add this to your ~/.txr_profile: (put-line (quip))
- brokencode 6y agoEvery time I try out a dynamic language, I miss static types. I just love them so much for refactoring and the ability to more fully describe how components should interact with one another. Then when I factor in the incredible performance and efficiency of a language like Rust, thinking about all the extremely difficult performance problems I’ve had to diagnose in even fast JIT languages like C# or modern JavaScript runtimes, I just can’t stomach the idea of choosing a language like that if I have another choice. So maybe Lisp would be a revelation, but I just can’t bring myself to care.
- FpUser 6y ago>"...learning Scheme ..., ...You will, however, be a much better programmer if you do and you will come to appreciate the beauty of these languages... Sorry but this could be said just about any language as this is very personal opinion. I for one think that knowledge of any particular language does not define being "much better programmer". Rather it is the good understanding of a general concepts and knowing how computers work. Author's opinion about performance is also one sided. For some tasks it matters not yet it is never good enough for others.
- vindarel 6y agoEverybody says Lisp is dynamic. OK but it's not Python. Don't forget that CL, and in particular the SBCL implementation, does pretty good compile-time type checking. Nice to note is that we get the warnings instantly because during development we can compile the function we are working on with a keystroke. Sure it is not a Hindley-Mindler type system (there's one in development: https://github.com/stylewarning/coalton/ https://github.com/stylewarning/coalton/) but it is "good enough". BTW, Atom and VSCode have pretty good plugins this day (https://lispcookbook.github.io/cl-cookbook/editor-support.html https://lispcookbook.github.io/cl-cookbook/editor-support.ht...) and CL might have more libraries than you think (https://github.com/CodyReichert/awesome-cl https://github.com/CodyReichert/awesome-cl). A lot is going one.
- chocimir 6y agoSeems like a lot of lisp fans here. Can you recommend any lisp repo to learn list "good practices" and "design patterns" from?
- mbrodersen 6y agoHere we go again. Another LISP fan boy articles. And the usual HN responses/arguments. LISP is and has always been a niche language with limited impact on the world. It was fun to learn and play with but I don’t want to use it for real work. A few will disagree. That’s fine. You do what you prefer and best of luck.
- bluepoint 6y agoCan someone please recommend a situation or common daily task that I can deal with using some version of lisp, in order to learn it little by little without forcing me to learn it all upfront and it is not just emacs? Like a shell replacement? Or some configuration manager? Or how do people actually learn lisp if it is not their job?
- Folcon 6y agoPerhaps Babashka[0]? It sounds like the closest thing to what you want =)... Alternatively you could just use cursive[1] or vscode[2] and follow the REPL guide[3]? There's Racket, which has it's own built in environment[4]. Perhaps you'd prefer Common Lisp[5] instead, I don't believe you need to use emacs? There are options =)... - [0]: https://github.com/babashka/babashka https://github.com/babashka/babashka - [1]: https://cursive-ide.com/ https://cursive-ide.com/ - [2]: https://marketplace.visualstudio.com/items?itemName=betterthantomorrow.calva https://marketplace.visualstudio.com/items?itemName=betterth... - [3]: https://clojure.org/guides/repl/introduction https://clojure.org/guides/repl/introduction - [4]: https://docs.racket-lang.org/quick/ https://docs.racket-lang.org/quick/ - [5]: http://www.gigamonkeys.com/book/ http://www.gigamonkeys.com/book/
- bluepoint 6y agoSo basically clojure x 5 and racket x 1. But what can I actually do in practice day to day?
- Folcon 6y agoWell I'm suggesting more clojure things because I know clojure, I've used a little of racket and it was fun, as well as done very little common lisp =)... I'm not sure what you mean by practice day to day? These are all things that you could work on in various ways on a daily basis if you want? Or do you mean kata-like things? If so there's 4clojure[0] or codewars[1], which does racket[2] and clojure[3] alongside a host of other stuff. If instead you're looking at a suggestion of what to work on, just pick a small project and regularly work on it, I've done lots of small data processing tasks, as well as developing small games, pick anything that interests you and play with it. I mean Mazes for Programmers[4] is a great book, perhaps rewrite it in any language that interests you? If that's not quite what you want, can you please clarify? - [0]: https://www.4clojure.com/problems https://www.4clojure.com/problems - [1]: https://www.codewars.com/ https://www.codewars.com/ - [2]: https://www.codewars.com/?language=racket https://www.codewars.com/?language=racket - [3]: https://www.codewars.com/?language=clojure https://www.codewars.com/?language=clojure - [4]: https://pragprog.com/titles/jbmaze/mazes-for-programmers/ https://pragprog.com/titles/jbmaze/mazes-for-programmers/