Hacker News new | ask | show | jobs
by informatimago 2783 days ago
Lisp:

- Avoids the use of gratuituous special characters: (a b c)

- use-dashes-in-symbol-names-instead-of-underline-so-its-easier-to-type (you can still use underlines or most any special character, but we like to keep it simple and easy).

- has a simple prefix syntax, that makes it easier to read and understand complex expressions: (loop (print (read (eval))) So no risk of confusing special characters or syntax: the meaning is clearly labelled by the first symbol of each expression!

- is not whitespace-sensitive, how can you see the size of whitespaces when you're blind?

- includes a reader and a pretty printer, so the code can be processed and formated automatically easily for either sighted or blind programmers.

- time-sharing was invented for the lisp REPL by the authors of lisp!

- as a good integration with emacs, which itself has a good integration with various tools such as screen readers, vocal commands, etc, and it's fully customizable.

http://cliki.net/ http://common-lisp.net/ https://en.wikipedia.org/wiki/Common_Lisp

5 comments

Well it seems that OP needn’t bother as this problem is already solved. You wrote (loop (print (eval (read)))). How does that go with a screen reader? Like:

Bracket loop bracket print bracket eval bracket read bracket bracket bracket bracket?

The key thing I think is balancing and shifting parens.

  (loop (print (eval)) (read))
Is quite hard to sound different from

  (loop (print (eval (read))))
And more crucially what about the difference between these two:

  (let ((x (f))) (g x) (g x))
  (let ((x (f)) (g x)) (g x))
(I think writing these on a single line makes them written more like they might be spoken)

On the other hand I think being able to modify the syntax structurally is a big advantage.

Going back to the topic of the OP I think a useful thing is putting functions after their arguments, and optimising to have single arguments or most non-main arguments being typically small. For example something more like

  read
  | eval
  | print
  | loop
And maybe some way to do arguments like

  read stdin 
  | eval some-environment 
  | print :pretty
  | loop
”Bracket loop bracket print bracket…”

A screen reader need not literally read what’s on screen. It typically doesn’t with prose, where punctuation isn’t spoken, but affects timing and intonation, and abbreviations often are expanded (iOS speaks “Dr. John St.” as “Doctor John Street”, for example, but “St. John Dr.” as “Saint John dee-ar”. MacinTalk used to know that ‘Dr’ means ‘Drive’)

So, it need not do that here, and could say

“call read, eval it, print the result, and call loop on print’s output”

, using intonation or voice to indicate the difference between content read from the screen and text describing it.

Farfetched? Maybe, but take a look at what screen readers do with html.

A screen reader that knows lisp semantics could go even further, and replace loop by repeat forever or something like it.

Requiring a sufficiently smart screen reader seems a bad way to go. 30 years on from CLtL we still don’t have a sufficiently smart compiler and I would argue that that problem is easier. One thing is that this works “better” with one-argument functions. Consider:

  (with-open-file (*standard-output* foo)
    (print x))
  (print y)
Vs

  (with-open-file (*standard-output* foo)
    (print x)
    (print y))
How do you read the first? As “print x, now with-the-file foo-open-as-standard-output it; then print y”? Or maybe “with foo opened as standard output, print x; now print y”?

This seems more reasonable but how do you write the second example? Like “with foo opened as standard output, print x; also print y”?

There are reasonably large semantic differences in such cases and many programming languages, including lisp, express them with subtle hard-to-pronounce differences in indentation or parenthising of expressions.

The language in question already has a dataflow/pipeline style by default, so expressions are generally read from left to right like this, except for some standard syntactic sugar like infix operators and flow control blocks like “if” and “match”:

    read eval print loop

    stdin read
    some_environment eval
    pretty print
    loop

    [1, 2, 3] [4, 5, 6] \(+) zip_with
    do (each_index) -> i, x {
      if (x = 7) {
        "Lucky seven!" say
      } else {
        ["Value at index ", i show, " is ", x show]
          concat say
      }
    }
> 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.

> is not whitespace-sensitive, how can you see the size of whitespaces when you're blind?

Can't indentation be handled by the IDE as having a meaning like "root class declaration", "function declaration", "nested block 1", "nested block 2"? Indentation has meaning and meaning can be converted. Even Xcode seems to be context aware as I can choose where certain code block shortcuts can or can't be executed. "This is a function", "This is a class", "This is a function in a class", "This is the root level", "This is a block one deep inside a function inside a class".

If you are willing to restrict a programmer to only look at accessible renderings of the AST, then there is probably nothing to consider, but a programmer's job is also handling what happens when a program does not parse.
This. Very few language designers understand the usefulness of being able to parse the code while it is incomplete/being typed out.

You end up with “hacks” to color your code vs a real parser to compile the code.

My compiler is error-recovering for this reason: you can still get correct syntax-highlighting data even if there are lexical errors, type information even if there are type errors, and so on.
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.
I love Lisp but every experienced lisper will tell you they don't see the parentheses, they look directly at the AST. Seeing the AST is, for me, highly visual (proper indentation is a must) and I wonder how a blind programmer could see that.
By using something like Paredit to navigate the source by list structure. Reading and navigating are kind of mixed together.
Blind people still have spatial reasoning.
They do, but their interface to the computer does not. Screen readers are inherently linear while a screen show two dimensions at once. Indentation is the primary way this happens in practice.

(I suppose it would not be impossible for a programming language to have 3d layout projected onto the 2d screen, but I'm not aware of any mainstream languages that do this. Significant whitespace is fairly common of course and nonsignificant indentation is more common still)

Good point. I imagine something like vim's "folding" feature could be used to interactively navigate source code with a screen reader by successively opening the folds of interest.
Using dashes in symbol names is problematic. It becomes impossible to differentiate

    my-var & my - var
As variable declaration or assignment looks very similar to the subtract expression... But if you enforce whitespace, then it's clearer I suppose...
There is a quite a lot of experience with dashes in names in FOSS because they are used in shell and Makefile programming, not only in Lisps.

Git commands have dashes: git cherry-pick.

Lots of hits with this: echo /usr/bin/-

The subtract expression in Lisp looks like

    (- my var)
because Lisp uses prefix notation. You would never run into ambiguities since there are no infix operators in Lisp at all.