Live data from Hacker News

Why Racket? Why Lisp?

practicaltypography.com

241–250 of 295 posts

Re: Why Racket? Why Lisp?

#241
post #87

Earlier quoted context omitted.

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…

> We've figured out how to do type systems This is probably the only valid point you have. > a lisp programmer from the '90s would be amazed by CPAN, never mind maven CPAN was started in 1997. A lot of Lisp programmers back then were also doing Perl, and a few people who were doing Perl then are doing Lisp now. Whoever is "amazed" by Maven should go jump off a bridge. > We have a bunch of reasonable approaches to eff…

[deleted]

Re: Why Racket? Why Lisp?

#242

Earlier quoted context omitted.

Sure, but those are one-line functions, which Python supports through lambdas. (Incidentally, some of them could be done with the operator module, without defining a new function). I would argue that once you write a multi-line function, it makes sense to give it a name and define it separately, which makes the "no multi-line anonymous functions" complaint less pressing.

Let me give you an example from my experience. I wrote an elegant parser definition library, used for parsing specialized output of ... never mind. It had hierarchic specifications (regular expressions etc) of parsing states, along with anonymous functions which stored away parsed data and sent the parser among different states. New juniors could after a few hours use this to quickly parse complex text documents. I c…

I'm a python guy and I totally agree that multiline anonymous functions greatly enhance the readability of code in certain applications. You can do everything with named functions, but it's not always the best method for conveying meaning.

I can live without multiline anonymous functions - but I'd make use of them if they did exist.

Re: Why Racket? Why Lisp?

#243

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…

"CLOS allows me to dispatch based on the types of all of the arguments." But can it dispatch based on the return value?

This is an example of dispatching on return type in Clojure - which is similar to Common Lisp.

http://stackoverflow.com/questions/22740178/clojure-dispatch...

(The Haskell purists point out that the dispatch isn't determined at compile time - form your own opinion about whether they have a point).

Re: Why Racket? Why Lisp?

#244
post #156

Earlier quoted context omitted.

Java/C++ are not the world. Your list is not the "features I think are new or cool or advanced about my programming language".

Fine, but can you name some language features that came out after the '90s that weren't in Lisp already? Excluding type systems?

Newer features tend to use type systems for part of their implementation because they're a powerful tool, but I feel the feature is its own thing. I'm excited about typeclasses, or anything else that solves the same problem. I'm excited about effect systems, which tend to be implemented as types but don't have to be. I hope to learn more about the matryoshka approach, which seems like the opposite thing or at least opposite emphasis to lisp's approach to metaprogramming (which emphasises doing the metaprogramming in the same language as the program itself).

Re: Why Racket? Why Lisp?

#245
post #199
post #54

Earlier quoted context omitted.

I didn't mind but note that he wrote http://practicaltypography.com . As for not missing stuff from Lisps, I think opportunities start appearing once you start using it more. But I agree, it doesn't prevent you from getting things done.. I don't see any Lisp in the Go space (native compilation, great networking features), so I find Go more pragmatic at this time.

> I don't see any Lisp in the Go space (native compilation, great networking features) What?!? Proper Lisps compile to native code, both JIT and AOT. Plus there are quite a few code commercial compilers with networking libraries. http://franz.com/products/allegrocl/ Don't judge Lisps by the open source alternatives.

Sorry, instead of "native compilation" I actually should have said "compiles to a single static binary".

Re: Why Racket? Why Lisp?

#246

Earlier quoted context omitted.

"CLOS allows me to dispatch based on the types of all of the arguments." But can it dispatch based on the return value?

This is an example of dispatching on return type in Clojure - which is similar to Common Lisp. http://stackoverflow.com/questions/22740178/clojure-dispatch... (The Haskell purists point out that the dispatch isn't determined at compile time - form your own opinion about whether they have a point).

I'm not a Haskell purist, but this isn't really dispatch on return type, this is manual inspection of manually provided metadata that just happens, in this case, to be (intended to be) a return type.

The return type isn't enforced, incidentally:

    user> (defn ^String my-function [^Integer foo] (apply concat (repeat foo "x")))
    #'user/my-function
    user> (my-function 3)
    (\x \x \x)
Whoops, I meant (apply str ...).

You also can't use the metadata on a function introduced by (defn ^ReturnType ...) in more or less any useful way, if you don't have a reference to the var itself. If you pass it to a function, you basically can't recover it:

    user> (defn do-something-interesting-with-return-type [f] (println "here's my metadata:" (meta f)))
    #'user/do-something-interesting-with-return-type
    user> (do-something-interesting-with-return-type my-function)
    here's my metadata: nil
    nil
Gone!

You can tell that something fishy is going on in the SO answer because the author has to pass in the function by name, #'return-string.

Re: Why Racket? Why Lisp?

#247
post #235

Earlier quoted context omitted.

Translating, with some minimal explanation: name :: constraints_for_type => type_of_name says "Thing named 'name' has type 'type_of_name' with constraints 'constraints_for_type'" So, minBound :: Bounded a => a maxBound :: Bounded a => a "minBound has type 'a' for any 'a', so long as 'a' is an instance of Bounded" These two are more values than functions, but the polymorphism happens the same way. If you use them wher…

I'm happy with myself for at least mostly getting what those were. I think I'd have to see more uses to really see the benefit, though. I am curious on minBound and maxBound. They seem to be the same... What distinguishes them? Or, you are just saying these are two values that are defined. And they can only be given a value of a type that can be bounded? Also, thanks for expanding!

minBound and maxBound are typeclass functions. Typeclass's are a similar to java interfaces.

So the Bounded typeclass is defined as

    class Bounded a where
        minBound :: a
        maxBound :: a
If you have a function and you want the argument to be Bounded you write

    allLessThan :: (Enum a, Bounded a)=> a -> [a]
    allLessThan x = [minBound..x]
Here `a` is a generic type. That implements the Enum and Bounded typeclass. You could just write `allLessThan x = [minBound..x]` and Haskell with infer the type classes by itself.

To implement the typeclass you use `instance`, like so:

    data MyType = A | B | C | D
    instance Bounded MyType where
        minBound = A
        maxBound = D

Re: Why Racket? Why Lisp?

#248
post #209

Earlier quoted context omitted.

That sounds like a feature that would both be awesome, and annoy the crap out of me. Are there examples where this actually leads to clearer code in the wild?

Several common typeclasses in Haskell have methods that are polymorphic in the return type, and I find it extremely useful. The bounds of a bounded type: minBound :: Bounded a => a maxBound :: Bounded a => a Converting an integer to an enumerated type: toEnum :: Enum a => Int -> a Casting between numeric types: fromIntegral :: (Integral a, Num b) -> a -> b Parsing a string into a value: read :: Read a => String -> a…

Rust also allows polymorphic return types.

    trait Bounded {
        fn min_bound() -> Self;
        fn max_bound() -> Self;
    }
Which can be used like so. Using type inference.

    fn add_min_to_set>(set: &mut S) {
        set.insert(Bounded::min_bound())
    }
 
Or you can be more explicit

    let mut i: u8 = Bounded::min_bound();
    while i 
Edit: spacing, s/++/+= 1/

Re: Why Racket? Why Lisp?

#249

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…

I know CL, I know Haskell. Really what OP said has nothing to do with refactoring (which is indeed painless in Haskell) but more to do with interactive programming.

Haskell is not terrible at interactive programming, but compared to Common Lisp and Smalltalk, it's garbage.

And this is exactly why it feels so kludgy and awkward when one tries to do bottom-up programming and organically evolve his program, the standard way of programming in CL and Smalltalk since they are image-based languages that are designed for exactly this.

This is one of the benefits that one with no prior experience in working in this way can quickly attempt to dismiss, but it's really THE most important reason for someone to use CL rather than Haskell. By having a language that allows you to program in a live environment, from the inside and keep the entire system running at all times, you become one with the machine. The feedback loop is kept extremely short, and you're in a constant state of flow.

It's too bad when people dismiss CL (and Smalltalk) based on trivialities (type systems) that are completely irrelevant in the grand scheme of things. Writing code in Haskell to me feels like a sterile, boring process similar to working out mathematical proofs. That's not to say that Haskell doesn't have its place. When the domain is well-defined and I know exactly what I want to do and how I'm about to do it, I'm more inclined to reach for Haskell or Ocaml than Common Lisp.

This is not the case for me most of the time however. When I program for myself, I find that I often have a general idea of what I want to do but no specifics in mind. It is then that I reach for Common Lisp in order to flesh things out and get inspired if you like. Or, similarly, when I'm doing a project that is hard, the domain uncertain, something with solutions that are not obvious ahead of time. Common Lisp and Smalltalk shine for these sort of problems, there is nothing else that comes close in my opinion.

Common Lisp but not Scheme/Racket I hear you say? Yes, this is indeed the case. Racket is just bad at interactive development of the kind I just described, because its developers deemed image-based programming 'too confusing' and removed all support for it from the IDE. This should also explain why it's not mentioned at all in the original article. Moreover, I find that in order for someone to truly appreciate how empowering image-based development is, one needs years of writing code in traditional non-image based languages. Which is why the best feature of Lisp in my opinion is swept under the rug by most newbie programmers who try out Lisp. They lack the experience to realize what they are missing.

Which is also why the original post author completely misses the point. The real power of Lisp comes from what I just described, not from 10 or 20 bullet points that one needs to side-by-side compare with other languages. By his admission he is a newbie programmer, and a newbie to Lisp too, made evident by his misunderstandings about the Common Lisp macro system vs Scheme "hygienic" macros. Hopefully he will grow and learn to appreciate Lisp for what it really is.

Finally, writing code in Common Lisp or Smalltalk makes me feel like an architect of my own private universe. This is exactly what programming should feel like, an organic process, fusing mind and machine together if you like.

Re: Why Racket? Why Lisp?

#250
post #235

Earlier quoted context omitted.

Translating, with some minimal explanation: name :: constraints_for_type => type_of_name says "Thing named 'name' has type 'type_of_name' with constraints 'constraints_for_type'" So, minBound :: Bounded a => a maxBound :: Bounded a => a "minBound has type 'a' for any 'a', so long as 'a' is an instance of Bounded" These two are more values than functions, but the polymorphism happens the same way. If you use them wher…

I'm happy with myself for at least mostly getting what those were. I think I'd have to see more uses to really see the benefit, though. I am curious on minBound and maxBound. They seem to be the same... What distinguishes them? Or, you are just saying these are two values that are defined. And they can only be given a value of a type that can be bounded? Also, thanks for expanding!

Hopefully hornetblack's answer shed some light, but the most important bit is that the most of the above were just type signatures. If they're part of a typeclass you would define them for a particular type in the instance. Otherwise, you'd define them generically using only other functions defined to generically work with the same constraints (or looser).

So in the case of minBound and maxBound in particular, you'd define them appropriately for a particular type when you declare a type to be Bounded.

Post reply on HN