Live data from Hacker News

Why Racket? Why Lisp?

practicaltypography.com

141–150 of 295 posts

Re: Why Racket? Why Lisp?

#141

Earlier quoted context omitted.

Only by not working with anonymous functions can anybody come up with such an impression. Python has at least 3 features that are not needed in languages that have proper support for anonymous functions and that are more expression oriented: 1. for comprehensions 2. the with statement 3. decorators You cannot work efficiently with higher-order functions until you have anonymous multi-line functions, period - also, Py…

> Python has at least 3 features that are not needed in languages that have proper support for anonymous functions and that are more expression oriented: > 1. for comprehensions Off the top of my head Scala, Erlang, and Haskell -- all of which are "more expression oriented" than Python (and all of which have robust support for anonymous functions including multiline anonymous functions -- the latter despite, like Pyt…

Well, Scala and Haskell's comprehensions are monad comprehensions, mapping to operations such as map, filter and flatMap/bind. Python's for comprehensions only work on things that are iterable, which IMHO is a severe design limitation and makes them less useful than they should be. Think at Async I/O abstractions, like futures / observables / iteratees, which are not iterables.

And yes, if Python makes it easier to work with higher-order functions and such combinators / operators become the norm, then we'll talk about 2 non-orthogonal and conflicting features.

Python is the only language I know that added for comprehensions before proper support for anonymous functions. All the other languages I worked with (including Clojure, to be on topic) had anonymous functions before the syntactic sugar built on top. Clearly Python has a problem here.

Re: Why Racket? Why Lisp?

#142

Earlier quoted context omitted.

> OTOH modern "researchey" languages, like Coq or even Haskell, are far far ahead of Lisp. I know Common Lisp well, and I honestly tried to learn and to use Haskell. I realized that Haskell has the advantage of a strong type system but it seems only to be useful for language research (compiler writing) and mathematical applications. Haskell is (in my case) almost useless for every day real world applications. It is a…

> It is a pain to align a whole Haskell project according to new requirements to make the whole system work again. I'll quote a recent tweet by Chris Done: "I feel like 80% of Haskell advocacy should involve screencasts of people refactoring large codebases." In my experience, refactoring is easier with a strongly typed compiler, not harder. It may take more time and work to get your program to "run" again, but the e…

Very much so. I do quite a bit of abuse to my C code to get similar (if weaker) assistance.

Re: Why Racket? Why Lisp?

#143

Earlier quoted context omitted.

Only by not working with anonymous functions can anybody come up with such an impression. Python has at least 3 features that are not needed in languages that have proper support for anonymous functions and that are more expression oriented: 1. for comprehensions 2. the with statement 3. decorators You cannot work efficiently with higher-order functions until you have anonymous multi-line functions, period - also, Py…

Can you please explain to me what's important about these functions being anonymous? Why, specifically, they shouldn't be given a name? How do you define working "efficiently with higher-order functions"? Given that Python fully supports higher-order functions, I am really curious what you could mean. I didn't downvote you, but it may have to do with your pointed assertion here, without anything in the way of an argu…

Imagine having to build functions with names for each while, if/else and foreach statements. Because that's what it feels like when working with async I/O in Python, a complete pain in the ass compared to other languages.

Scala sample:

      cache.get[String]("name").flatMap {
        case Some(value) => value
        case None =>
          database.query("names").head.flatMap {
            case Some(id, value) =>
              cache.set("name", value, 10.minutes)
                .map(_ => value)

            case None =>
              Future.successful("Anonymous")
          }
      }
 
BTW, in this sample, for comprehensions are not that useful. But if you're using Scala-Async, you can write that in a style resembling blocking I/O:

      async {
        val cached = await(cache.get[String]("name"))

        cached match {
          case Some(value) => value
          case None =>
            val fromDB = await(database.query("names").head)
            
            fromDB match {
              case Some(id, value) =>
                await(cache.set("name", value, 10.minutes))
                value

              case None =>
                "Anonymous"
            }
        }
      }
In both cases multi-line anonymous functions are leveraged.

Re: Why Racket? Why Lisp?

#144
post #6

I sure hope the giant, hideous, obtrusive diamonds inserted into the text to denote a hyperlink doesn't catch on as a trend. It's a great way to break the flow of the text and irritate your readers. As for the idea of Lisps, well, it sure seems neat. But I've literally never run across a situation where I needed my code to edit itself. I've never run across a situation where the lack of an everything-is-an-expression…

Being able to write new control structures can come in very handle. Imagine if your language of choice had exceptions/errors, but didn't have try/catch/finally. Now imagine you could just implement that structure as a command/macro. That's a rather extreme example, but it does highlight the power available.

Re: Why Racket? Why Lisp?

#145
Some practical features I enjoy in CL:

1. Conditions and restarts: As far as error handling in programs go this is the most rock-solid system I've encountered. You can tell the system which bits of code, called restarts, are able to handle a given error condition in your code. The nice thing about that is you can choose the appropriate restart based on what you know at a higher-level in the program and continue that computation without losing state and restarting from the beginning. This plays well with well structured programs because the rest of your system can continue running. Watching for conditions and signalling errors to invoke restarts... it's really much better than just returning an integer.

As a CL programmer using SLIME or any suitable IDE, this error system can throw up a list of appropriate restarts to handle an error it encounters. I can just choose one... or I can zoom through the backtrace, inspect objects, change values in instance slots, recompile code to fix the bug, and choose the "continue" restart... voila the computation continues, my system never stopped doing all of the other tasks it was in the middle of doing, and my original error was fixed and I didn't lose anything. That is really one of my favorite features.

2. CLOS -- it's CL's OO system. Completely optional. But it's very, very powerful. The notion of "class" is very different than the C++ sense of struct-with-vtable-to-function-pointers-with-implicit-reference-to-this. Specifically I enjoy parametric dispatch to generic functions. C++ has this but only to the implicit first argument, this. Whereas CLOS allows me to dispatch based on the types of all of the arguments. As a benign example:

    (defclass animal () ())
    (defclass dog (animal) ())

    (defgeneric make-sound (animal))
    (defmethod make-sound ((animal animal))
      (format t "..."))
    (defmethod make-sound ((dog dog))
      (format t "Bark!"))

    (make-sound (make-instance 'animal))
    (make-sound (make-instance 'dog))
Will print "..." and "Bark!" But the trivial example doesn't show that I can dispatch based on all of the arguments to a method:

    (defclass entity () ()) ;; some high-level data about entities in a video game
    (defclass ship (entity) ()) ;; some ship-specific stuff... you get the idea.
    (defclass bullet (entity) ())

    ;; ... more code

    (defmethod collide ((player ship) (bullet bullet))) ;; some collision-handling code for those types of entities...
    (defmethod collide ((player ship) (enemy ship))) ;;; and so on...
Conversely...

    Ship::collide(const Bullet& bullet) {}
    Ship::collide(const Ship& ship) {}
Where collide is a virtual function of the Entity class requiring all sub-classes to implement it. In the CLOS system a method is free from the association to a class and is only implemented for anyone who cares about colliding with other things.

The super-powerful thing about this though is that... I can redefine the class while the program is running. I can compile a new definition and all of the live instances in my running program will be updated. I don't have to stop my game. If I encounter an error in my collision code I can inspect the objects in the stack trace, recompile the new method, and continue without stopping.

3. Macros are awesome. They're like little mini-compilers and their usefulness is difficult to appreciate but beautiful to behold. For a good example look at [0] where baggers has implemented a Lisp-like language that actually compiles to an OpenGL shader program. Or read Let Over Lambda.

One of the most common complaint I hear about macros (and programmable programming languages in general) is that it opens the gate for every developer to build their own personal fiefdom and isolate themselves from other developers: ie -- create their own language that nobody else understands.

Examples like baggers' shader language demonstrate that it's not about creating a cambrian explosion of incompatible DSLs... it's about taming complexity; taking complex ideas and turning them into smaller, embedded programs. A CL programmer isn't satisfied writing their game in one language and then writing their shaders in another language. And then having to learn a third language for hooking them all up and running them. They embody those things using CL itself and leverage the powerful compiler under the floorboards that's right at their finger tips.

Need to read an alternate syntax from a language that died out decades ago but left no open source compilers about? Write a reader-macro that transforms it into lisp. Write a runtime in lisp to execute it. I've done it for little toy assemblers. It's lots of fun.

... this has turned into a long post. Sorry. I just miss some of the awesome features CL has when I work in other languages which is most of the time.

[0] https://www.youtube.com/watch?v=2Z4GfOUWEuA&list=PL2VAYZE_4w...

Re: Why Racket? Why Lisp?

#146
post #88

If you took a Common Lisp programmer from the early to mid 90s in a time machine to today, very little about current programming languages would seem novel or an advance over what he or she was using then. I think this is a reason for much of the smugness of Lisp programmers. Whatever features you think are new or cool or advanced about your programming language, Lisp probably got there first.

Yeah, I think that most programming language developments since the '70s have involved putting some ideas from C and Lisp in a blender for a few minutes. The main advances IMO have come in the areas of IDEs, build systems, and language ecosystems. I'm a mediocre programmer at best who's done a bunch of Lisp in the past. I loved using it, but these days I rely on Ruby for getting things done because it gives me what I…

And smalltalk in that mix too.

Re: Why Racket? Why Lisp?

#147
post #87

If you took a Common Lisp programmer from the early to mid 90s in a time machine to today, very little about current programming languages would seem novel or an advance over what he or she was using then. I think this is a reason for much of the smugness of Lisp programmers. Whatever features you think are new or cool or advanced about your programming language, Lisp probably got there first.

Nonsense. We've figured out how to do type systems. We can even fully infer types if you're willing to accept some quite reasonable restrictions on how polymorphic your code is. We have a bunch of reasonable approaches to effect tracking, which Lisp folk used to have to do by hand (that story about the T garbage collector sounds like the most unmaintainable piece of code I've ever heard of). We know how to solve the…

Type systems are the one exception, but it still remains broadly true that Lisp was way ahead of everybody. There are many things we could talk about besides type systems:

Lambda expressions - just now reaching Java and C++, been in Lisp forever

Garbage collection – (obviously)

Turing-complete (edit: fully evaluating) macro systems – I've heard C++ is moving in this direction (not sure to be honest) but Lisp is still ahead on this

Gradual/optional typing – Others have been moving towards this, CL has had it forever

Interactivity/REPL – e.g. Swift may finally give a REPL to systems programmers, Lisp has had it forever

Dynamism/late-binding – Lisp is still way ahead on this, goes hand-in-hand with interactivity

Everything serializable – Lisp is still ahead

Structured code editing (paredit) – Lisp is still ahead, not sure anyone else even knows what this is

Multiple inheritance – I think this is coming to Java finally

Image systems (hibernating a running process) – Still ahead

Condition system / advanced exception handling – Still ahead

Functional programming techniques (map, reduce, etc.) – Still ahead

Keyword arguments – Recently added to Ruby, Lisp has had them forever

Re: Why Racket? Why Lisp?

#148
post #9
post #6

I sure hope the giant, hideous, obtrusive diamonds inserted into the text to denote a hyperlink doesn't catch on as a trend. It's a great way to break the flow of the text and irritate your readers. As for the idea of Lisps, well, it sure seems neat. But I've literally never run across a situation where I needed my code to edit itself. I've never run across a situation where the lack of an everything-is-an-expression…

As someone who writes Lisp (well, Clojure) every day and does not particularly enjoy it, I find the common complaint about parentheses to be a non-issue. In fact, I'm not sure I've heard of anyone who wrote any significant amount of lisp code and came away talking about parentheses. This seems to be mostly a reaction from people who've read a bit of Lisp without using it. Then again, I just noticed that I type ( and…

Why don't you enjoy it?

Re: Why Racket? Why Lisp?

#149

Earlier quoted context omitted.

> It is a very good choice if I know the algorithm and data structures exactly in advance. I'd say the exact opposite: the great strength of Haskell lies in its powerful support for abstracting things out so that these things can easily evolve independently. The hard part of taking advantage of this seems to me to be that we're used as programmers to not having those tools, so it is hard to get used to thinking about…

I understand that. I really like Haskell but I wonder if the type system is actually too strong for the real world. Generalizing algorithms sounds nice but if Haskell is so powerful, why is the Haskell community still not able to provide a convenient working package manager? Perl has CPAN, Ruby has Gems, Lisp has ASDF and Quicklisp but Haskell is still stuck with buggy Cabal. I have been in the Cabal hell many times.…

Wait, are you suggesting that the Haskell type system is too strong to write a package manager? That makes no sense. A much more valid reason that Cabal is annoying is that Haskell is a compiled language that uses static linking, unlike all the languages you mentioned, which are interpreted.

Re: Why Racket? Why Lisp?

#150

Earlier quoted context omitted.

> It is a very good choice if I know the algorithm and data structures exactly in advance. I'd say the exact opposite: the great strength of Haskell lies in its powerful support for abstracting things out so that these things can easily evolve independently. The hard part of taking advantage of this seems to me to be that we're used as programmers to not having those tools, so it is hard to get used to thinking about…

I understand that. I really like Haskell but I wonder if the type system is actually too strong for the real world. Generalizing algorithms sounds nice but if Haskell is so powerful, why is the Haskell community still not able to provide a convenient working package manager? Perl has CPAN, Ruby has Gems, Lisp has ASDF and Quicklisp but Haskell is still stuck with buggy Cabal. I have been in the Cabal hell many times.…

Cabal is not buggy. Or, more precisely, Cabal Hell is not the result of bugs in cabal. It is the result of 1) static linking with cross-module inlining, 2) a lot of diamond dependencies, and 3) a lot of breaking changes in the ecosystem.

1 is arguably an artifact of Haskell - performance really suffers if you can't do this - but also something that's arguably desirable generally.

2 is the result of the ability to provide abstractions and tools that are useful in a phenomenal number of contexts (mostly a good thing) and the failure of the language-as-standardized to provide these directly (mostly a bad thing).

3 is the consequence of an active community that values experimentation, is willing to try new things, and places an emphasis on Getting It Right. This is actually more mixed a blessing than it sounds.

Post reply on HN