7 ms·
See what I did there? Despite the good intention, no, because you're trying to explain lisp using lisp. If I knew lisp, I wouldnt need you to explain what code
by quadcore 9y ago
See what I did there?
Despite the good intention, no, because you're trying to explain lisp using lisp. If I knew lisp, I wouldnt need you to explain what code-as-data means.
Maybe it's time for a different strategy. As a matter of fact, I've tried to explain code-as-data using OOP just this morning: https://news.ycombinator.com/item?id=16389514 https://news.ycombinator.com/item?id=16389514
- 3pt14159 9y agoYour linked comment is great but it sweeps one thing under the rug. There is a reason lisp didn't win. We put up with it (I use emacs, so I have to write it at times) but in general it's harder to reason about transformable lists of code than it is about plain old objects and their methods. Almost every problem elegantly solved in lisp has a parallel elegant solution in Ruby (using blocks, dynamic method definition, or otherwise) and I generally find the resulting code both more maintainable as well as more readable. Sometimes with lisp things get "spooky" in a way that just doesn't happen in practice with Ruby, even if it theoretically possible to say redefine String#inspect. Though I completely agree that inheritance gets clunky and I strongly favour composition.
- quadcore 9y agoI agree, OO is ultimately good enough. Problem is, coding using OOP is like printing ideas on paper: you dont wanna do that because it's hard to discard ideas that's been printed on paper. You wanna use post-its instead because post-its are easy to discard. Refactoring is hard using OO and refactoring is the key difference between waterfall and good software development. Refactoring is easier, even fun, using functional programming because the medium is more malleable.
- flavio81 9y ago>I agree, OO is ultimately good enough. And Common Lisp has arguably the most flexible and powerful OOP system around, CLOS. Why don't write OOP in Lisp? It is done successfully by lispers around the world.
- kamaal 9y ago>>Why don't write OOP in Lisp? Because most people understand OO as some kind of namespace scheme. Basically to bunch together functionality under a name(class). When people talk of OO, most of them, are actually talking about modularizing code.
- deleted 9y ago[deleted]
- goatlover 9y agoThat's modules or packages. OO includes inheritance, polymorphism and treating objects as black boxes (little computers in Alan Kay's mental model) with messages they respond to, or APIs for working with that kind of object. It's a lot more than simply being a namespace. It's also that idea that an object has state and behavior, with appropriate semantics for dealing with both (depending on the language). Objects (with classes or prototypes) act as a new kind of data type bundled with functionality for handling that data, which is more than just putting a bunch of functions and variables into a namespace. So you can treat your class/prototype as a built-in complex data type. You're extending the type system in an encapsulated manner. It's not called Namespace Oriented Programming
- Retric 9y agoRefactoring lisp gets harder the more complex the program gets. Approachs that are clever and reasonable for a 1000 line program can become hell in a 100,000 line program. Effectively programming languages get less powerful the longer the program becomes. Breaking up programs into more powerful little pieces is never a clean separation and you need to make real trade offs between more separation and more power.
- DannyB2 9y ago> Refactoring lisp gets harder the more complex the program gets. I have found that to be true in dynamically or duck typed languages. I have found it to be not true in strongly typed languages with good tooling. Example: refactoring large java program in, say, Eclipse. > Breaking up programs into more powerful little pieces is never a clean separation I would tend to disagree, to some extent. In Lisp(s), there are lots of small, sometimes obviously correct functions that do powerful operations on abstract data structures. Part of the reason for this is that unlike OOP (and I write Java daily) which has lots of data structures, Lisp has very few. In OOP you model your domain with new classes. In Lisp you model your domain with combinations of a few basic structures. (In Clojure...) List, Set, Array, Map. Maybe you have layers of separation. With one layer being between primitives that manipulate your domain objects, and another layer being the logic that decides how and when to do the manipulations. I want to solve a puzzle. The puzzle board has various states, pieces, and operations that transform a board into a new board state. Then the logic layer is an algorithm or algorithms written using the layer of primitive manipulations. That logic layer may, in fact, use canned algorithms, because the data structures are common primitives. Say, sort, search, A* search, depth first, breadth first, reduce, map fn over a sequence, etc. You can end up with a large program. But it can be a lot easier to reason about than, say, a large Java program. It seems to take more deliberate effort to keep a large java program easy to reason about.
- Retric 9y ago> layers of separation Layers are exactly the kind of power trade-off I am talking about. If code X can't(1) impact code Y, you have less freedom in code X because it can't do some things, but can more easily reason about code Y. (1) either as an actual limit or a self imposed one. Granted, in theory you may be able to find perfect points of separation such that you don't lose power by doing so.
- skybrian 9y agoYou'll have to substantiate that, because refactoring Java is pretty easy if you have a good IDE. In many other languages, you're lucky if you can reliably rename a function without having to manually check for false positives, let alone something complicated like change signature or extract superclass. Also, in any statically typed language, you can fall back on changing the function and using compile errors to tell you all the calls that need to be updated.
- quadcore 9y agorefactoring Java is pretty easy if you have a good IDE rename a function Refactoring in software development is not the same as refactoring in IDEs. Refactoring in software development is changing existing code. Specially the architectural parts.
- skybrian 9y agoCould you explain what you mean? As far as I know it's the same process. Changing the architecture often involves a large number of small, safe code changes, many of which can be handled by IDE's and other software tools.
- taeric 9y agoUltimately, I assert the truth is that we don't know why it didn't succeed more. So, speculation is just that. Take mine that is about to follow as more of it. I would be delighted to know of a way to test some of these ideas. Lisp didn't win because it used to cost money to get a good implementation, and there was no company throwing tons of marketing at it. Now, I specifically don't think it is a case of worse ones winning. The reality is they are all impressive. Many newer languages got to make tradeoffs that were not viable years ago. They are still nicely done and I'm glad they are here.
- oregontechninja 9y agoI theorize lisp isn't used more because of the massive amounts of parenthesis. I think the concepts are great, and it's a great language for certain task, but my right pinky finger hurts just looking at it. New programmers and developers look at that and compare it to something like Go, Python, or JavaScript, and all those look easier to write (although deceptively complex in places) and are 1000x easier to read. A prettier lisp would do the world good, but things like sweet expressions are confusing for absolutely new devs to implement and veterans feel like they don't need them. Short version: Lisp wallows in obscurity thanks to it's "look"
- lloeki 9y agoSweet expressions are a bridge too far IMHO. I would be perfectly content with merely getting rid of those dangling ))))) thanks to simple parentheses insertion† based on a single very simple indent/dedent rule: > if the next line is indented, anything from the beginning of the current line starting at the indent till a dedent matching the current line indent is wrapped in one set of parentheses. Seriously, what about this? defmacro deftag [name] list 'defn name ['& 'inner] list 'str "<" (str name) ">" '(apply str inner) "</" (str name) ">" deftag html deftag body deftag h1 deftag p Now what about the DSL that now downright looks like slim-lang?: html body h1 "Hello World" p "How's it going?" Or look at that: defn qsort [coll] if (empty? coll) coll let [pivot (first coll) remainder (rest coll)] concat (qsort (filter (partial > pivot) remainder)) [pivot] (qsort (filter (partial <= pivot) remainder)) (even the qsort ones could be removed, but they balance [pivot] somehow) I'm perfectly happy with writing (+ 1 2 3) or whatever, the only parentheses that bother me are those that are perfectly delineated by indentation already, just as newlines perform the same duty as semicolons. They're just duplicated noise††. † Other languages perform similar semicolon insertion, with varying degrees of success and ambiguities (JS==terrible, Go==terrific). †† I know, you're supposed to get used to it and "unsee" them. I know, "paredit!". But seriously if you have to write a tool solely to produce noise, and you have to get accustomed to ignore said noise, well, somehow, something's off.
- flavio81 9y ago>There is a reason lisp didn't win. We put up with it (I use emacs, so I have to write it at times) but in general it's harder to reason about transformable lists of code than it is about plain old objects and their methods. Sounds like your experience was with Emacs Lisp, a very clunky Lisp. >Though I completely agree that inheritance gets clunky and I strongly favour composition. Check out CLOS (Common Lisp Object System), which is based on multimethods/multiple dispatch. It is very different to "traditional" OOP (most other languages except Julia and Dylan)
- taneq 9y ago> Sounds like your experience was with Emacs Lisp, a very clunky Lisp. The lisp that didn't win is not an acceptable lisp.
- firethief 9y agoSo we have only unacceptable lisps. That doesn't really have any bearing on the fact that Emacs Lisp is a bad unacceptable lisp to use as an example if you want to talk about the potential of the Lisp family of languages.
- kamaal 9y ago>>Sounds like your experience was with Emacs Lisp, a very clunky Lisp. Emacs lisp is actually a very practical, easy to use and fun lisp to do a lot of great work in. Richard Matthew Stallman notes: It was Bernie Greenberg, who discovered that it was (2). He wrote a version of Emacs in Multics MacLisp, and he wrote his commands in MacLisp in a straightforward fashion. The editor itself was written entirely in Lisp. Multics Emacs proved to be a great success — programming new editing commands was so convenient that even the secretaries in his office started learning how to use it. They used a manual someone had written which showed how to extend Emacs, but didn't say it was a programming. So the secretaries, who believed they couldn't do programming, weren't scared off. They read the manual, discovered they could do useful things and they learned to program. From: https://www.gnu.org/gnu/rms-lisp.en.html https://www.gnu.org/gnu/rms-lisp.en.html Of course if secretaries and regular office people with no prior exposure to programming or STEM education can program Emacs using Emacs Lisp and do useful productive stuff. Programmers most certainly can.
- ledgerdev 9y ago> in general it's harder to reason about transformable lists of code than it is about plain old objects and their methods. I would say it's more fundamental, simply a large majority find it difficult to interpret and reason about trees, rather than sequential operations.
- lloeki 9y ago> has a parallel elegant solution in Ruby (using blocks, dynamic method definition, or otherwise) > Sometimes with lisp things get "spooky" in a way that just doesn't happen in practice with Ruby Over here the long run practical experience with Ruby on a couple or three significantly involved codebases and assorted dependencies taught me over the years that such solutions are elegant for a month then end up being a hell to maintain, debug, and trace through. We definitely encountered my lot of "spooky" things in Ruby-land. For maintainable code on the long run we now vastly favour boring, non-dynamic, explicit, down-to-earth code. Dynamic solutions, DSLs or dependencies making use of those now therefore have to sustain solid justifications and heavy scrutiny before being greenlighted.
- kamaal 9y ago>>in general it's harder to reason about transformable lists of code than it is about plain old objects and their methods. Well actually no. Other languages make it easier for beginners to get started, but they just keep you there. Because its harder to do that the advanced stuff in those languages. >>Almost every problem elegantly solved in lisp has a parallel elegant solution in Ruby (using blocks, dynamic method definition, or otherwise) and I generally find the resulting code both more maintainable as well as more readable. Its other way around. We solve trivial library stitching problems in other language, and the we try the same in Lisp and don't see the point. But then that is also a kind of mental handicap. You can do things beyond a point because tools are making it harder for you to keep going forward. And then you use Lisp with the same philosophy and don't see the point.
- blain_the_train 9y agoI didn't understand those concepts in Ruby until I learned clojure and then I found them to be overly complicated.
- didibus 9y agoI don't think that's the reason lisp didn't win. I'd say it's more because of Price, Slowness and Unfamiliarity. I've used Clojure professionally now for a year, and I'm not finding the problem you mention to exist. Maybe its exclusive to emacs lisp?
- taeric 9y agoI actually added in that other post that party of the appeal is that you can easily explain most lisp using lisp. This is a strong reason to keep the common syntax tiny. Learning to eval and what it means to apply code gets you ready to understand pretty much all of it. That is, I suspect many of us like that you can often think of code in terms of other code. In that language. Don't fall into the trap that this is the end game, though. I am not arguing that. Just that it is a helpful step along the way.
- pdkl95 9y ago> you're trying to explain lisp using lisp You may prefer this[1] explanation that uses XML (ant) to explain the basic idea. [1] http://www.defmacro.org/ramblings/lisp.html http://www.defmacro.org/ramblings/lisp.html
- kamaal 9y ago>>because you're trying to explain lisp using lisp. There is no other way to explain it. Simply put: (1 2 3 4) Is a list (add-all 1 2 3 4) Is also a list (add-all 1 2 (add-all 3 4)) Is also a list In the above example the nested list is data to the main list. And there is all there is to it. In the last example, code just went in as data to another list(code as data). Which is also code.
- joshuamorton 9y agoThis is a terrible example. I see no reason what you just gave isnt isomorphic to sum(1,2,sum(3,4)) Which is not "code as data". There's no reason for me to believe that the data isn't evaluated before being passed to the outer list.
- kamaal 9y agoTrue, but. sum(1,2,sum(3,4)) isn't a list. so sum() isn't a list member and things passed to it aren't list either. Lisp code is more consistent.
- joshuamorton 9y agoNo, it's equally consistent. In lisp, everything is a list. In the example I just gave, everything is a scalar. Like I said, this is a very bad justification for lisp.
- kamaal 9y agoEverything isn't a scalar in the example you mentioned. sum() is a function and other things are numbers. In lisp there is only rule, everything is a list and the first element of the list is a operation to the remainder of the list. This is basically the whole language.
- joshuamorton 9y ago>In lisp there is only rule, everything is a list Well no. `print` is not a list. `4` is not a list. `(print 4)` is a list containing the scalars `print` and `4`. The mechanics of lisp then say "apply the thing in the first position to the remainder of the list", but that's just notation. In python, `sum` is a function that can apply to a splatted sequence of scalars (note that it can also apply to a list, but this is different), so `sum(1,2,3,4,5)` works. It returns a scalar value. In lisp, `+` is a function that can apply to a list. It returns a scalar. `(+ 1 2 3)` resolves to `6`, not `(6)`. This is no different than python (or C actually, for this example). I understand lisp, I understand why "code is data" is a powerful tool. Your example is a bad one, it does not demonstrate why "code is data" is a powerful tool, because it does not actually demonstrate that code is data.