HNHacker News
TopNewBestAskShowJobs

kbp

807 karma · joined October 19, 2016

submissionscomments
kbp··on Why MIT uses Python instead of Scheme for its undergraduate CS program (2009)
> If you call a function that happens to refer to a variable that only exists in the calling code, it will work

Could you give an example of what you mean by this?

kbp··on Why MIT uses Python instead of Scheme for its undergraduate CS program (2009)
I think it's very common in discussions like this for people to conflate 'Python vs Scheme' with from-first-principles (lambdas, explicit recursion) vs building-real-things as pedagogical approaches. In a lot of people's minds, Python is its standard library and Scheme is explicitly cdring down lists.

I think the problem is that SICP's pedagogical approach (which people confuse with the language Scheme) is, in a way, pretty close to starting with assembler. You spend a lot of time exploring computation (with lambdas as your basis) at a very low level before you graduate to anything that feels "real" or practical.

kbp··on Ruby 2.6
One nice feature of blocks is that `return` will return from the enclosing function, rather than the block, so you can write:

    def find(x, l)
      l.each do |y|
        return y if y == x
      end
    end
Where in Lisp you'd use (return-from find x) and in other languages you might pass in a continuation or use a special return value protocol. It's a nice solution for higher order functions that are supposed to feel more language-level.

Also, you can pass functions as arguments like in other languages; lambdas behave like you would expect them to.

kbp··on Better Clojure formatting
Trailing whitespace also makes jumping to the end of a line or moving back over a newline not take you where you expect, which is annoying when it breaks your flow.

It's only ever a mild nuisance (in that scenario or for diffs), but the fact that it's trivial to automate seems to make it a no-brainer to me.

kbp··on Perchance to Scheme (2016)
There's an SRFI for generalised set! that lots of implementations (Racket, Guile, Chicken, etc) support: https://srfi.schemers.org/srfi-17/srfi-17.html
kbp··on Perchance to Scheme (2016)
> A little more detail here: it has a lot more namespaces than that.

And I don't think the function/value split is even the most important namespacing it does with regards to macro hygiene ("functions are less likely to be shadowed" always struck me as a kind of weak argument). Symbols being namespaced under packages is a much more robust solution:

    CL-USER> (defmacro m (x) `(car ,x))
    M
    CL-USER> (defpackage :p)
    #<PACKAGE "P">
    CL-USER> (in-package :p)
    #<COMMON-LISP:PACKAGE "P">
    P> (cl:macroexpand '(cl-user::m x))
    (COMMON-LISP:CAR X)
CL:CAR isn't accessible in P, so (car x) by itself would signal UNDEFINED-FUNCTION, but the macro works correctly. Defining a P::CAR function wouldn't change anything.

That isn't just for functions, either, eg if you do:

    (defmacro awhen (pred &body body)
      `(let ((it ,pred))
         (when it ,@body)))
(where you want to leak IT), and then import AWHEN but not IT, you'll get an error that YOUR-PACKAGE::IT isn't bound to anything when you try to use it; it doesn't matter that AWHEN-PACKAGE:IT is bound.

I don't disagree with anything you said, I just think it made CL macros look flimsier than they are.

kbp··on OCaml for the Masses (2011)
> it seems to prefer blocks to lambdas

What does this mean?

kbp··on If I were to invent a programming language for the 21st century
The first example saves you from thinking about indexing; arrays and loops are relevant in both.
kbp··on Introduction to Clojure (2013)
> Then parenthesis, square brackets, and curly braces help me easily find lists, vectors, maps, and sets just from a quick visual scan of the code.

How often do you want to do that? Wouldn't you be more interested in what the variable is called or who it's passed to?

kbp··on Ask HN: How should a programming language accommodate disabled programmers?
You misunderstood. I only used blindness for the specific example where you used blindness (the only specific example of affecting someone with a disability that you gave; why would that be harder for a blind person?). Most of your points didn't explain how they related to accessibility, they just said your way was easier and better without explaining how or to whom. That's what I was asking.
kbp··on Ask HN: How should a programming language accommodate disabled programmers?
By using something like Paredit to navigate the source by list structure. Reading and navigating are kind of mixed together.
kbp··on Ask HN: How should a programming language accommodate disabled programmers?
> Bracket loop bracket print bracket eval bracket read bracket bracket bracket bracket?

It would say "left paren" and "right paren." It's not terribly hard to keep a stack in your head of how many close parens you're expecting in some context. Manually managing brackets in long or deeply nested forms (in any language) is a pain whether you can see or not. As you said, pseudo-structural editing is a big advantage.

> (I think writing these on a single line makes them written more like they might be spoken)

Screen readers can announce indentation levels.

kbp··on Ask HN: How should a programming language accommodate disabled programmers?
I don't understand what most of these points have to do with accessibility. Why would a blind person have more trouble with the fact that "x + y" might be addition or string concatenation than a sighted person? Why would compile-time type errors be more useful to a disabled person? And so on. It just feels like your opinions on a lot of language design holy wars where disabled people are just as likely to disagree with you as anyone else.

> Don't worry about superficial tooling support. Solve the language design problems, the hard problems first.

That's mostly what people have been doing for the last most-of-a-century, and it's led to a lot of software being much harder to use for disabled people.

kbp··on Ask HN: How should a programming language accommodate disabled programmers?
I think the places where it does use punctuation are a lot more friendly to people with low vision than most languages: you never use awkward combinations like '),' where it's easy for the comma to blend in with the bracket (the comma in ',(' stands out a lot more), you don't write things like 'if (!pred)' where the bang similarly blends in, etc. The general preference for descriptive names over punctuation for operators is really helpful, too, as it lets you rely much more on the basic shape of the word.
kbp··on Clojure at Netflix (2013) [slides]
> And then, as others have mentioned, it's hard to find Lisp programmers. Most programmers are turned off by the [...] immutability.

Most Lisps have very little in the way of immutability (except for C-style "undefined behaviour if you modify it"). They tend to be very dynamic, living systems, and making everything mutable is one way of enabling that. Some people favour a functional style, and a functional style is more common in some dialects like Scheme, but it's almost always essentially "imperative code with good support for higher order functions," and most Lisp programs have always mutated state freely.

There are exceptions, but they are exceptions.

kbp··on Clojure at Netflix (2013) [slides]
> but as a LISP most people aren't going to be excited about using it.

I agree that Clojure has a lot of strong points and that s-expressions probably put a lot of people off, but as a Lisp programmer, I was very disappointed in Clojure's debugging/interactive development story (and I've heard that from a lot of others). It feels more like using a typical scripting language compared to the traditional Lisp/Smalltalk experience, and even there, a typical scripting language would at least give useful backtraces. As it stands, I think a decent number of conventional Lisp programmers would also worry about large Clojure programs being unmaintainable unless they're superbly written.

kbp··on Not Lisp again (2009)
> Because Common Lisp is such a complex language, there are a few places where I have chosen to simplify things to better meet the needs of beginners. For example the 1+ and 1- functions are banished from this book because their names are very confusing.

Are they? I don't know if I remember hearing anyone complain about those before.

kbp··on Not Lisp again (2009)
`input` isn't READ, it's READ-LINE. READ reads one Lisp form, even if it's more than one line, and even if there are more than one on one line.

Python's `eval` also isn't sufficient here. For example, you cannot enter that loop you wrote at the prompt it provides when you run it, even if you write it on one line. You could use `exec`, but then everything would print as `None`.

There's the larger point about forms vs strings and printing readably and such, but this doesn't even provide the same basic functionality that a Python programmer would expect from a REPL.

kbp··on This Old Lisp
> I do consider strong typing mandatory. That was my problem with Ruby

What isn't strongly typed about Ruby?

kbp··on This Old Lisp
You can specialise generic functions on built-in classes in standard CL. Lisp methods are specialisations of generic functions; they don't belong to a class in the way methods do in eg C++. The issue you're talking about is that not all functions are generic functions in Common Lisp, and you can't specialise ordinary functions.

There's nothing stopping you from doing

    (defmethod add ((x number) (y number)) (+ x y))
    (defmethod add ((x string) (y string)) (concatenate 'string x y))
or whatever (multiple dispatch, too), and you could even call it + instead of ADD if you wanted (but not COMMON-LISP:+, so other code would continue to work; your packages could import your + instead of the standard one).
kbp··on A guide to rhythm in web typography
It seems weird to say "And because (as we learned earlier) we read word by word, too much spacing between words breaks this rhythm" as a reason to avoid justified text, and then immediately after say that you can temper bad justification by telling it to auto-hyphenate words, so now "masters" is "mas-\nters" and "artisan" is "artis-\nan". That seems like a much bigger hit to quick readability than the poor justification they were initially complaining about. Their "better" example is easily worse than their "bad" example for readability, I would think, and to my eyes it's a bigger difference than between their "bad" and "good".

The spacing between words is better in the "better" version than the "bad" one, but if the point is that we read by words, then breaking up words across lines seems like a much graver sin than bad inter-word spacing.

kbp··on How Lisp Became God's Own Programming Language
> (It also looks like this was written on one of those ancient systems that didn't support lowercase and only allowed 6-character filenames)

A limitation that also led to 'Schemer' dropping the R.

kbp··on How Lisp Became God's Own Programming Language
> Nonsense, the enterprise programmer of the time was programming in C or C++ and was making heavy use of macros.

C macros are entirely different in use from Lisp macros. C macros are written in a text substitution language that knows virtually nothing about C, whereas Lisp macros are written in Lisp and operate on regular Lisp data structures. Even trivial things are very difficult to get right with C macros, whereas very complicated things are often elegant and simple with Lisp macros. It's an entirely different experience. They're not at all comparable.

C programmers learn to fear macros because C macros have a lot of problems. Few of those carry over to Lisp macros.

kbp··on How Lisp Became God's Own Programming Language
> What?? In chapter 4, you write your own Lisp interpreter. If they had chosen C++, would you be writing a C++ compiler? Or a Lisp interpreter in C++?

That's in chapter 5 :)

Exercise 5.51. Develop a rudimentary implementation of Scheme in C (or some other low-level language of your choice) by translating the explicit-control evaluator of section 5.4 into C. In order to run this code you will need to also provide appropriate storage-allocation routines and other run-time support.

kbp··on How Lisp Became God's Own Programming Language
SICP only uses a small subset of Scheme, and it's only using it as a minimal vehicle for exploring computer science. You can absolutely learn the amount of Scheme used in the book as you go (that's why they chose that subset: it's not supposed to distract you from the topics they're discussing; they're trying to get as close as they can to simple axioms), but you won't really "know Lisp" (or Scheme) in practical terms when you're done, either (you'll be well-equipped to pick it up quickly, though).
kbp··on The Problem with Lisp
> I found python and ruby much easier to read and pick up. One reason for this is, that programmers are not allowed to define new irregularities.

The mechanisms they give you are more than enough, though. Ruby especially favours this kind of thing a lot; how many people really have any idea what's going on (and how many of them beyond "something to do with method_missing and instance_eval") when they write something like:

    FactoryBot.define do
      factory :foo do
        bar { 'baz' }
      end
    end
I don't think understanding that it's all blocks and method calls wins you that much.
kbp··on The Problem with Lisp
> I can be 100% sure that: `self` is an object with an attribute rfile, `=` is the assignment operator, there is a method `readline` that get's called with an integer argument 65537.

I think this is overstating things, eg, the class `self` belongs to might have defined __getattribute__, __setattr__, etc, so the things you know for 100% certain about that line of code in isolation don't really tell you anything about what it does (self does not definitely have that attribute, and that might not be assignment, at least); most of that you assume based on common sense, conventions, figuring the code is "normal", etc, the same as in Lisp. And that's ignoring a trivial case like if the previous line was `f('''`, where f may or may or not call eval.

kbp··on A new version of Common-Lisp.net has been launched
There's a lot of approaches that might work; using something other than a list for your collection is just one (although it is often appropriate).

If you just want option types that you can use with lists, the most direct way to do that is by encoding None as NIL and Some x as (list x), which still lets "no value" be false, makes "some value" true even for the empty list, and pairs nicely with pattern matching libraries like Optima (which people yearning for option types might also be interested in):

    (match maybe-value
      ((list value) value)
      (nil 'not-found))
That seems cleaner to me, too: if you want to use the empty list for "no value", then why wouldn't you use a list containing the value you want for "some value"? This also scales to more than 1 optional value, and doesn't require any special casing for lists, if you're thinking of them as values like any other; the NIL thing doesn't even come into it. Maybe this sort of confusion comes from how null works in languages like Java?

It might also make sense to represent the absence of a value by just not assigning a value. For example, CLOS instances won't bind their slots to anything by default, so trying to read them will signal UNBOUND-SLOT. You can check if it's bound beforehand with SLOT-BOUNDP, and CLOS does differentiate between a slot not being bound and a slot not existing at all. If you're working on something interactively and UNBOUND-SLOT gets signalled, by default execution will pause, and Lisp will offer strategies for resolving the issue, like entering in a value to use instead for just this call, entering in a value to store in the slot for later use, just aborting the computation, etc. That's a pretty nice experience (and just the tip of the iceberg) that people coming from more static languages might not even think about.

If you're returning a value to someone, maybe it would be best to work like GETHASH does and return two return values, the first either a value pulled from the table or a default, and the second a boolean indicating whether the key was present or whether you got the default. Because Lisp has multiple return values (not like Python or Ruby where you're really just returning a sequence and then destructuring it), callers that are fine with the default and don't care if it was actually present can just use the primary return value, and since the default is provided when you call GETHASH, you can also do nice things like (incf (gethash key hash 0)).

There's other ways too, of course, but it's impossible to say what would be the most appropriate solution without knowing more about the problem. (eq nil '()) returning T is not a bug, so the issue must be higher up the call stack. If that seems like a dealbreaker to someone, they're probably just looking at it in isolation and not thinking in terms of Lisp as a whole.

kbp··on Asterisks in Python
Oh, or more directly:

    [f(a, b) for a, b in it.permutations(some_list, r=2)]
And then you could also use itertools.starmap instead of using a comprehension at all, if you wanted.
kbp··on Asterisks in Python
itertools has functions that make that sort of nesting unnecessary (and some would say make the code clearer), eg:

    import itertools as it
    [f(a, b) for a, b in it.product(some_list, some_list) if a is not b]
← PreviousPage 3 of 12Next →