1. 42

A common-accepted “hard thing” in programming is naming things: variables, function, files, and more. Are there programming languages that have been designed to reduce naming things?

The examples I’ve come up with so far include:

  1. Spreadsheets. I don’t know if they were designed to reduce naming, and I think several modern spreadsheets allow named ranges, but still: one can get by without naming anything.
  2. Visual programming languages. (Thanks, @goodger!) Here, too, I don’t think any of these have been designed with the express purpose of reducing naming, but maybe?
  3. Perl’s special shortcuts (Thanks @bio_end_io_t!) I was unaware of these, but this looks like what I had in mind. I think bash has some similar constructs? Then the next question is: does it make programming easier (i.e. thinking more about programming and less about naming) or harder (i.e. naming things is documentation).
  4. APL (Thanks, @molloy!) I haven’t dug into this at all.

Other examples or ideas?

EDIT: Fascinating responses so far…I’m learning a lot! Some follow-up questions:

  1. Is a spreadsheet the only example of something that automatically gives (bad) names to everything? Has this been done in a text-based language?
  2. In your opinion, are fewer names a good or bad thing? I suppose it is a tension between two extremes, like most things…or can naming be reduced without harming readability or maintainability? (As an example, while I love method chaining, it can often be hard to debug something in the middle of a chain, at least in JavaScript. On the flip side, having tens of intermediate and similarly-named variables can be difficult to keep in one’s head.)
  1.  

  2. 21

    All the stack based ones: Forth, Cat, PostScript, etc

    1. 2

      I only recently saw my first example of a stack-based language. My thought was that it seems terribly difficult, as a programmer, to keep in mind what the stack contains at any given point in time. Is that something one gets used to over time?

      1. 8

        I found it fun, when learning Forth, to actually work things out using a physical stack of index cards and a pencil. But yeah, you get used to it pretty quick.

        1. 2

          In my experience, words rarely cause more than 7 changes to the stack (like, rot pops 3 and pushes them back in a different order, for 6 changes, while dup pops once and pushes the same thing twice for a delta of one change), so if you get used to chunking in terms of only what a word pops and pushes, you can almost treat it like imperative with implicit args.

        2. 1

          I think it is difficult at first but once you get used to use combinator words (common in e.g. Factor) that probably becomes just as obvious as using map and fold in functional languages.

        3. 1

          Forth may omit variable names, but makes up for it with many word names.

        4. 17

          Pointfree style in ML-family languages (e.g. Haskell) lets you avoid naming things, though the style was designed more for general conciseness than that specifically.

          1. 9

            Similarly, pipelines in shell scripts let you avoid naming intermediates.

            (Pipelines are a combination of pointfree style and array language style, in that well-designed pipelines are side-effect-free and the programs in the pipeline mostly implicitly iterate over their input.)

            1. 2

              There’s also a tool called Pointfree which auto converts code into “pointfree style” - e.g.

              \a b c -> b * c
              
              const (*)
              

              And there’s also Pointful, for the other way around.

              1. 1

                I think Haskell strikes a very fine balance here. Both point-free and pointful styles are very ergonomic, so you tend to name precisely those things that would be better off named.

              2. 12

                It’s not fashionable, but perl took from Larry Wall’s linguist background the concept of ‘it’ - i.e. the “thing we are talking about”.

                It’s spelt “$_” explicitly in perl, but also many operations use $_ implicitly if no arg is given (e.g. regex/sub, print, etc). Also the looping constructs (for/map/grep) bind $_ to the ‘current element’. So you can:

                print for @lines;
                

                and have:

                • the for loop loop over each element in the array/list
                • bind ‘$_’ as a (mutable!) alias to each list element
                • invoke print (which defaults to $_ in the absence of other args)

                Or:

                while (<STDIN>) {   # Loop over each line on input
                  chomp;            # remove trailing \n
                  s/foo/bar/;       # regex-and-replace
                  say;              # Print to stdout with \n
                }
                

                The schwartzian transform (https://en.wikipedia.org/wiki/Schwartzian_transform) uses this, and also the convention/feature that in a block/lambda passed to ‘sort’, the two items under comparison are ‘$a’ and ‘$b’. Which are hence sufficiently magic that you should never use them for any other purpose in perl :-)

                1. 4

                  There’s also a great section on anaphoric macros in On Lisp.

                  1. 2

                    anaphoric macros create a name that must be referenced to actually refer to it though. I guess in Perl, it’d be roughly equivalent to super lisp pseudocode:

                    (defparameter *it* (make-parameter #f))
                    
                    ;; str defaults to *it* if called without arg
                    (define (chomp (str *it*))
                       ....)
                    
                    (define (say (str *it*))
                         ...)
                    
                    (let loop ((*it* (get-line)))
                        (if *it*
                           (begin
                              (set *it* (chomp))
                              (set *it* (sub ...))
                              (say)
                              ...)))
                    
                  2. 2

                    The jQuery library for JavaScript also supports a similar feature. In a function passed to $.each, this will be the current array element, and in an event handler, this will be the element that the event was fired on (which I think matches the browser’s DOM event handlers). The handlers can also take those same variables as optional function parameters if you want to name them.

                  3. 11

                    Not a language, but a language feature: in Elixir, there’s a capture operator & that will wrap functions in an anonymous function and also can be used to refer to the nth argument of a function. For example:

                    add_one = fn x -> x + 1 end
                    

                    is replaced by

                    add_one = &(&1 + 1)
                    

                    This helps avoid naming arguments unnecessarily.

                    Read more

                    1. 10

                      This is one of the features inspired by Clojure. In Clojure, #(foo %) is short for (fn [x] (foo x))

                      1. 2

                        You also have %1, %2 etc. in Clojure.

                      2. 4

                        There’s also the pipe operator |> which passes the result of the expression on the left side as the first argument of the function on the right.

                        https://elixirschool.com/en/lessons/basics/pipe-operator/

                        1. 1

                          Scala also has _, which in _ + 1 is an alias for x => x + 1, or in the case of f(_), is an alias for x => f(x). Oleg Kiselyov has an interesting take on it.

                          1. 1

                            I’m not that familiar with Elixir (only having done a few basic distributed algorithms in it), but this feature has piqued my interest even further in the language, thanks for the handy tip!

                          2. 9

                            APL-oids are are probably the biggest example (J, Jelly, k/q and so on), followed by stack based and/or concatenative languages (Forth, PostScript and 8th being examples of the former, Cat, Joy and Factor being examples of the latter). After that would likely be functional programming languages (especially with things like Haskell’s point free notation), and then function chains in OO-esqe languages.

                            Having played around with a stack based language, naming things is hard to get right in the documentation sense, but not naming things is also hard, but in the getting-it-right sense, because then you have to track a lot of implicit stuff, and/or have the compiler show you what the structure at a given point looks like.

                            I don’t recall the name of it at the moment, but I ran across a programming language that allowed you to just re-use the name of the type if you didn’t have more than one instance of that type in a given scope, which was certainly a novel approach.

                            1. 6

                              The FP language hasn’t been mentioned yet.

                              John Backus’ Turing award lecture discusses FP in detail.

                              1. 1

                                I found an interpreter for FP and have started porting it to MacOS. The repository is here.

                              2. 4

                                Most functional languages fit this category. Clojure is a good example of a language that provides great tools to avoid naming transient variables with its various threading macros.

                                1. 3

                                  It’s not really a functional language, but fwiw this is also common in modern R code via magrittr pipelines.

                                  1. 1

                                    You still have to name functions.

                                    1. 3

                                      If you want recursion you’ll have to name functions, unless you want to write out the combinator from first principles every time. (Naming the combinator fix would be cheating!)

                                    2. 1

                                      That’s a good point…I hadn’t thought of that, and I use method chaining all the time! Do you find in Clojure that it is difficult to debug the intermediate states of a “thread” (not sure what term is appropriate here) such that intermediate variables would be more convenient? (I guess I’m thinking from a perspective of JavaScript’s method chaining, which seems similar…and it is somewhat frequent that I need to log intermediate values in a .map.reduce.filter chain.)

                                      1. 4

                                        In the standard library, you can debug -> using another (-> doto prn) and you can debug ->> using (#(-> % doto prn)), although I usually use taoensso.timbre/spy for this. A simplistic solution to work for both ->, and ->> is (defn debug [x] (prn x) x).

                                    3. 4

                                      Brainfuck and Piet are esoteric but AFAIK both are designed to work by addressing memory locations, either on a stack or by address, and they have no notion of a named variable or function.

                                      I don’t know whether eschewing naming was a design goal for them, but neither of these languages are especially easy to use directly.

                                      [edit added]

                                      Make has “implicit rules” and “automatic variables” that can prevent the need for naming targets, or even writing any code at all. For example, even without a makefile you can type “make foo” and if “foo.c” exists, it will run the compiler using implicit rules.

                                      1. 4

                                        Rust encourages shadowing so that multiple transformations of a variable can keep the same name. So the point it is in time refers to the form it takes but they all refer to the same idea, without forcing you to pollute your namespace. I guess this would be error prone in an interpreted language, but the compilation step averts those issues.

                                        1. 4

                                          Re follow-up question 1, check out “gensym”. It’s mostly a lisp thing.

                                          1. 4

                                            Seen your update including perl. You ask:

                                            Then the next question is: does it make programming easier (i.e. thinking more about programming and less about naming) or harder (i.e. naming things is documentation).

                                            and the answer to that is that if you’re writing big, maintained perl programs, you typically use explicitly named lexicals. Except where the scope is very short. Not least because the un-named abbreviations have dynamic not lexical scope:

                                            #!/usr/bin/perl
                                            use 5.10.0;
                                            
                                            for (qw/foo bar baz/) {
                                              do_stuff();
                                              say;      # output $_
                                            }
                                            
                                            sub do_stuff() {
                                              say "wtf $_" if /z/;
                                            }  
                                            
                                            $ perl tt.pl
                                            foo
                                            bar
                                            wtf baz
                                            baz
                                            
                                            
                                            1. 4

                                              In your opinion, are fewer names a good or bad thing?

                                              As someone who used Clojure’s ->> macro a lot and uses |> in OCaml way more than my coworkers I would say… I prefer to have fewer names and only name “standalone” data, not stuff in the middle of being transformed. In Clojure one can prn in the middle of the chain just fine (same as in JavaScript), in OCaml I usually don’t even need to do that since the type system guarantees that my chain type checks and most of the times I’d mess up it is with the shape of the data not its exact values so the type checker catches most issues I might have when building up pipelines.

                                              I personally also like to shadow names that I expect not to be using in the future to signal to the reader that the previous binding is “dead” and can’t be accidentally used to do the wrong thing.

                                              So yes, I think fewer names is less things to keep in mind and potentially misuse. Also, naming some intermediate byproducts is yields bizarre, nondescriptive names sometimes, which already signals that whatever you are naming is probably something that doesn’t really need a name.

                                              1. 3
                                                1. 3

                                                  Swift has a few things, e.g. for anonymous functions you can do the shell-like sum = arr.reduce(0, { $0 + $1 }). The others I can think of (concatenative langs in particular) have already been mentioned. :)

                                                  1. 2

                                                    For extreme examples, look up golfing languages, like cjam, golfscript, and pyth.

                                                    1. 2

                                                      In Haskell you can just name your variables x, f, m, a, and so on, provided you’re writing at a sufficiently high level of polymorphism, so you don’t usually have to come up with names like handlerFactoryManager.

                                                      Agda is even better because you tend to use all kinds of Greek letters, Unicode dingbats, and box-drawing characters.

                                                      1. 1

                                                        In your opinion, are fewer names a good or bad thing?

                                                        The principle underlying all of the XP practices is “if it hurts, do it more often”. So, what hurts about naming things?

                                                        1. Being at the correct level of abstraction. I told you this function calculated the totalSalaryBudgetForDepartment(), so why am I looking at x and xs? Conversely I told you this function calculates totalValueOfNumericPropertyInCollection(), so why am I looking at it in my budget calculator?
                                                        2. Finding meaningful and distinct names at the same level of abstraction. When WebObjects switched to Java, almost any application contained the line: Session session = (Session)Session.session();
                                                        3. Showing relevant information and hiding irrelevant information. This is a subjective problem: I have had multiple situations where I have wanted to call a method e.g. computeAnswer and somebody else has wanted to call it fetchAnswerComputingIfNecessary when we have both been describing a thing that computes the answer but also memoises.

                                                        My opinion is that on balance, more names where they are good names would be a good thing, because they are the ladders that help us up and down between the multiple levels of abstraction in our software. But, as is the case with other forms of documentation, bad names are worse than no names and many developers have chosen to stick with no documentation rather than learning about producing the good.