
If You're Not Writing a Program, Don't Use a Programming Language [video] - azhenley
https://www.youtube.com/watch?v=Fq5EQBFLEC8
======
zackmorris
I wasn't exposed to spreadsheets until a few years into college back around
1996 or 1997 maybe (I had been programming in C/C++ for 7 or 8 years by then).
I wasn't taught matrix math until pretty late in the curriculum, I want to say
junior or senior year. Also I was lucky to have a semester of Scheme but they
were transitioning to teaching Java around the time I graduated (I don't know
if they ever switched back). And this was for a computer engineering degree at
one of the best state universities for engineering in the US.

Honestly I think it might be time to phase out teaching imperative and object-
oriented programming. Most of the grief in my career has come from them. I
don't care if they're where the jobs are. The mental cost of tracing through
highly-imperative code, especially the new implicit style of languages and
frameworks like Ruby and AngularJS (which have logic flows having no obvious
connection to one another, or transition through async handlers connected by a
convention which isn't immediately obvious to the developer) is so high that
the underlying business logic is effectively obfuscated.

I think we should get back to fundamentals. Maybe start with the shell and
explain why separate address spaces connected by pipes are such a powerful
abstraction, maybe touch on the Actor model, maybe show how spreadsheets and
functional programming are equivalent, and how even written notation is
superfluous. Really focus on declarative programming and code as media, and
how that made the web approachable by nontechnical folks before it was turned
into single page application spaghetti. There are so many examples from
history of where better ways were found before the mainstream fell back into
bad habits.

~~~
Retric
Computers are still imperative, so all functional code is _arguably_ syntactic
sugar over that core causing a lot of leaky abstractions to show up all over
the place.

I think the problem with Object-oriented programming is it's taught to soon.
Start with Imperative then Functional then toss object oriented into your
senior year.

~~~
adw
A ton of problems in software engineering, I am convinced (and isn't wild
generalization one of the marks of our field! I at least want to own my own
hypocrisy here) are communication problems. Nearly all the interesting ones,
anyway.

And one of them is _axioms no-one ever communicates_. I've worked with
programmers with at least three markedly different axiomatic bases, for want
of some less pretentious – and less exaggerated-for-rhetorical-effect – way of
putting this:

* `Electrical engineers`. "Computers are imperative because computers are just circuits". Functions, objects, mathematical abstractions in general are fundamentally leaky and to be regarded with deep suspicion. Paradigmatic languages: C, asm.

* `Pure mathematicians`. Computers are abstract machines for manipulating symbols. Programming is set theory; hardware is an implementation detail. State is just an artifact of insufficient precognition. Paradigmatic languages: ML, Haskell.

* `Approximators`. These folk rarely come from computer science backgrounds; instead they tend to come from "technical computing", meaning sciences, engineering, economics, statistics and the like. They're also on the rise, because this is the group of people who "get" machine learning. Computers are fancy and very fast slide rules; their job is to run approximate numerical methods very fast. The only true types are `float` and `bool`; programming is translating linear algebra into algorithms which estimate the correct results stably with bounded error, ideally by calling the right bits of LAPACK. Paradigmatic language: historically, Fortran; these days, whatever array language is embedded in the libraries they're using, surrounded by Python or (if statistician) R.

The point is; these are three markedly different worldviews and _none of them
are any more fundamentally wrong than the others_ – they're all useful and
wildly incomplete. So unless you can get agreement – or at least empathy –
within your team, you're going to spend a lot of time talking past each other.

~~~
shalabhc
So where do Smalltalkers and Lispers go?

~~~
lispm
Part of the Lisp tradition was the 'knowledge engineer'. Identify the
knowledge and processes necessary for problem solving and implement a machine
for that: logic, rules, semantic networks, ... Bring the programming language
near to the problem domain.

------
dmreedy
I believe these are the kinds of ideas that might actually create a genuine
_engineering_ culture in software development. Until we start applying this
kind of rigor to our work, I don't believe the title of "Software Engineer" is
justified. It doesn't have to be TLA+; it doesn't have to be any particular
tool or technology or pattern or whatever. But the attitude that rigor and
formal technique is _worth_ the additional effort is something that I don't
find often in the current culture, or at least the narrow slice of it that
I've experienced.

The quote at the end sums it up nicely. Coding _should_ be the easy part. A
sentiment everyone tends to nod their head along with, but rarely has the
discipline and willpower to implement.

~~~
batteryhorse
I disagree that engineering is the best approach to writing software. I think
this is the fundamental difference between people who prefer static vs dynamic
typing. People who prefer static typing are more likely to say that one
solution is more "correct" than the other, whereas dynamic typing allows more
flexibility.

I see writing software to be similar to writing music. Music has rules and
structure, but leaves room for creativity and allows two or more highly
skilled musicians to be able to play music together and improvise.

Static typing imposes limitations (kind of like government), whereas dynamic
typing gives you more freedom (but requires responsibility)

~~~
TeMPOraL
It's fine when we're talking about leisure-paced art, done by people who
deeply care about what they do. But the reality of software development is
different. Software is written primarily by people who treat it as a job, and
care about it (on average) about as much as they're paid to - and they're
being paid by people who don't care about the product, but its ability to make
money. Software written in such environment is (unlike music) powering systems
that affects livelihoods, health and safety of other people. With the market
pushing quality down, we need a countering force pushing it up.

Or, imagine dropping all regulations around planes, cars, food, health care or
infrastructure. Would things be handled better by artists, free to pursue
quality? Maybe. Will majority of the providers be artists, or even _care_
about quality? No.

~~~
gota
Just a sidenote, reading your comment it occurs to me that I have no idea -
and as far as I can tell never read anything about - what is the proportion of
code that is done 'for fun' versus 'for pay'.

If anyone has any survey or any discussion on that please reply so I can take
a look at it. I'd like to confront my expectations with data

------
jventura
I don't know who the target audience was, but I found the lecture very
confusing and the message really badly passed (he keeps talking what is
already written in the slides, etc.). I had to play it at 1.6x the regular
speed so I could move on with it.

Regarding the topic, I think there are multiple ways of expressing ideas,
where math happens to be just one of them. For instance, for the Greatest
Common Divisor example, you could just say that "if x equals y the cgd of x
and y is x, else you need to keep subtracting the smallest number from the
largest until both are equal".

You could use simple math, such as:

    
    
                 / x if x=y
      gcd(x,y) = | gcd(x-y, y) if x > y
                 \ gcd(x, y-x) if x < y
    

Or you could use code:

    
    
      def gcd(x,y):
          if x==y:
              return x
          elif x>y:
              return gcd(x-y, y)
          else:
              return gcd(x, y-x)
    

In essence, what I want to say is that his definition of beauty and power may
not match mine, but I will not argue that math can be more succinct or allow
for better abstractions in many cases.

In the end, I got the feeling that he is trying to "sell" us his TLA+
approach.

~~~
maccam912
I think he was trying to show that the TLA+ approach led to easier proofs of
the base case and inductive case. I don't think he's arguing that your gcd
program is bad. That's what I'd use. But TLA+ might be a good tool to break
out when you need to have a solid proof that the code you wrote will always
end up getting you the right answer. We probably don't need it for side
projects, but it's nice to know that AWS has some proofs around that the
services I use there can't end up in an inconsistent state as a result of any
coding logic.

------
georgewsinger
_TLA+ vs. Functional Languages._ I once tried to understand how TLA+ could be
useful if your implementation language was primarily functional (i.e.,
Haskell). I wrote to Leslie Lamport on his mailing list and he actually
responded with what I think was a very informative dialogue:
[https://groups.google.com/forum/#!searchin/tlaplus/George$20...](https://groups.google.com/forum/#!searchin/tlaplus/George$20functional%7Csort:date/tlaplus/7vxzRBAR6TQ/AU_p_BwCFwAJ)

I hope TLA+ gains more popularity. It's an interesting technology.

~~~
heydenberk
You should submit this thread to HN!

------
nkoren
Meta-comment: if you're producing a video of a presentation with lots of
equations on the slides, PLEASE make the slides larger than a postage-stamp. I
wanted to be able to follow this in detail, but was unable to do so because
the slides were illegible, and following them verbally was something that even
tripped up the presenter.

~~~
mickeyr
There's a better version here:

[https://hitsmediaweb.h-its.org/Mediasite/Play/abdee6c8696a48...](https://hitsmediaweb.h-its.org/Mediasite/Play/abdee6c8696a48288ce8c3e004c50cd41d?playFrom=13826&autoStart=false&popout=true)

------
jononor
I think we need reusable (formal) specifications for it to become adapted
widely. If that could be done in a way similar to how we today use open source
libraries of reusable code, findable via a package manager, it might fly.
Writing formal specs from scratch each and every time is too time consuming
relative to the gain, except for in a few critical usecases.

~~~
pron
If you use TLA+ you'll see why this is likely unnecessary. A _huge_
specification, made at three levels of detail, can run to 2500 LOC. Because
you have so much control over the level of detail, and because the language is
so expressive, there is little need for reuse (except maybe for some common
definitions, e.g. of different consistency levels, which are very short but
can be non-trivial).

~~~
jononor
How do you mean unnecessary? Surely writing such a specification takes many
man hours, even after having spent many man hours to learn TLA+? Can none of
this effort be encapsulated into something reusable?

The desire for reuse is not driven by a want to reduce LOC, but hours spent.

~~~
pron
Most of the effort writing TLA+ is about the _what_ not the _how_. In general,
we know how to make code reuse effective when a lot of implementation hides
behind a thin API, but in TLA+ there is no implementation in a meaningful
sense. A library of requirements would make about as much sense, and while
maybe it's something that could be done, I'm not sure we know how to do it
(but if you've seen something like it, I'd love to see it). So maybe it's an
interesting idea, but I'm not sure how it would be done. Aside from a utility
module that I like including in most of my specs, I never "reuse" modules as
is. I often do, however, copy-paste-modify pieces of other specifications,
which, I assume, is not unlike how lawyers reuse portions of contracts.

I guess there are occasionally pieces that could be used as-is (e.g. a
description of a lossy network), but those tend to be so simple, that looking
for them in some Maven-central for spec modules would probably more effort
than writing them from scratch every time.

------
rixarn
I'm more with Mike Acton in this respect; the hardware is the platform, not
the software. And in that sense... In order to really make good use of your
hardware you need to understand your hardware and code accordingly. I doubt
you can achieve great performance on a language that focuses entirely on
mathematical representations without considering the hardware.

So yeah... you end up with a beautiful mathematical expression ... but runs
way slower than an "ugly" equivalent in c code.

~~~
thrmsforbfast
_> In order to really make good use of your hardware you need to understand
your hardware and code accordingly_

Understanding your hardware is only half the story. To use the hardware
effectively, you also need to have mathemaitcal insights into your problem.

A rather extreme proof of this fact is bubble_sort.c vs. quick_sort.java. I
think it's clear which of those is using the machine more effectively...

 _> I doubt you can achieve great performance on a language that focuses
entirely on mathematical representations without considering the hardware._

Plenty of very high level languages have extremely effective compilers. SQL
query engines come to mind.

 _> So yeah... you end up with a beautiful mathematical expression ... but
runs way slower than an "ugly" equivalent in c code._

TLA+ et al. are about primarily about _correctness_ and designed for
situations where correctness is hard to get. Beauty is relevant only in-so-far
as it helps one the path to correctness.

But yes, hand-craft code will get you your GIGO much faster :)

~~~
rixarn
I agree, you need mathematical insight too you can have that and still code as
we do now without sacrificing a way to express solutions that consider things
such as cache locality. With what he proposes, that’s simply not possible.
There’s a very interesting talk by scott meyers about how important cache
locallity is to the point where, in practice, for some problems, big oh
analysis breaks because of cache misses. So an algorithm that is faster by
those standars gets its ass kicked by a “slower” one just because of cache
misses.

~~~
thrmsforbfast
_> With what he proposes, that’s simply not possible_

There's nothing special about the cache hierarchy. It's just one more thing
that can be reasoned about using mathematics. If you care about cache
locality, you can write down the semantics of an optimizing compiler and prove
that this compiler uses the cache hierarchy in an optimal way. Why would that
be impossible?

Not only is it possible, but it might even be _easier_ in high-level
languages. For example, the argument has even been made that the importance of
cache locality is really an argument in favor of higher-level functional
languages.

But again, with respect to TLA+, this is all a red herring and completely
misses the point. The more important point re: TLA+ is the GIGO/correctness
problem. I don't give a damn how cache-efficient my algorithm is if it's _not
even computing the correct results_!

------
moron4hire
I think he makes an assumption that is fundamentally incorrect in the vast
majority of cases: that the people owning the programs care about
specification.

There is a huge, dark-matter body of software development that goes on in the
world under the thumb of megalomaniacs whose only metric of productivity is
how much unpaid overtime his programmers are working.

------
lake99
Looks like an interesting talk, but are the slides available online somewhere?

Edit: Never mind. I found the slides he had made for the same talk given
elsewhere.
[https://drive.google.com/open?id=1sZciK13LDh4rBFwbRCk0Oylga3...](https://drive.google.com/open?id=1sZciK13LDh4rBFwbRCk0Oylga3NtNH6k)

------
mad44
A summary of the article the talk was based on and some discussion on this.
[http://muratbuffalo.blogspot.com/2018/07/if-youre-not-
writin...](http://muratbuffalo.blogspot.com/2018/07/if-youre-not-writing-
program-dont-use.html)

~~~
jkingsbery
"It's quite likely that during their lifetime, machine learning will
completely change the nature of programming."

\- Why is this the case? Even if it's true, it hardly seems obviously true.

~~~
about_help
Because soon you will just ask your computer to build your code after giving
it a few parameters. Then you will proof read and sanity check the code and
spend more time optimizing and working on the really hard problems.

~~~
xamuel
Assuming you could concretely nail down all the vague terms you just used, I'd
be willing to bet real money we won't have anything remotely like that within
the next 10 years :)

~~~
err4nt
I think a lot of people who suggest things like this use the image of a
computer writing code, or generating code in a programming language that
didn't exist, when I think in reality what's more likely to be implemented is
something more like Automator for Mac OS:
[https://www.youtube.com/watch?v=Z56PBanBSx4](https://www.youtube.com/watch?v=Z56PBanBSx4)

In Automator, pre-made blocks of code that do different things (filesystem
manipulation, data transformations, application control, machine control) can
be assembled into a workflow, and configured with options. It is possible that
something like that could be given a speech-based UI (rather than keyboard and
mouse) and the result is that pre-existing code is assembled and configured.
That's a lot more realistic.

------
aidenn0
I haven't watched the full talk, so let me know if this gets explored by
Lamport, but...

Here's a thought: PL researchers seem to generally agree that typed languages
are superior to untyped languages, yet programmers tend to prefer untyped
languages to typed languages, to the point where Java and C++ have the
fanciest type systems in common use, with ML being the closest thing to an
academic language that gets significant use.

It seems intuitive to me that typed languages are bad for exploring
algorithmic design, so could this where the disconnect is? Most programmers do
not know what algorithm their final product is going to have when they start
and dynamic languages are much more ergonomic when you are exploring he design
space.

Certainly if I knew exactly what program I was going to write when I started,
I could probably write it in haskell; indeed once I have a working program,
translating it to haskell is often fairly mechanical. However much of my time
is spent trialing algorithms that may or may not be a good fit for the problem
I'm solving and in those cases I feel like haskell gets in my way, while lisp
gets out of my way.

I don't know if there is any practical takeaway from this, but it does seem to
at least give an explanation for the divide between PL theory and practice.

~~~
drb91
> programmers tend to prefer untyped (dyanamic?) languages to typed languages

I have not found this in practice. I have found that some developers prefer a
dynamically typed language for certain situations, like rapid prototyping.
However when _maintaining_ code bases, I've found that developers often bemoan
the lack of static types to assist in small modifications to code they haven't
touched in a while.

Personally I love the ability of static, declared types to organize
assumptions about code; I also love the ability to do terrible things to types
to shoehorn poorly designed third party libraries into my codebase. I don't
see it as a developer preference so much as dependent on the problem domain.

~~~
pixelrevision
For me it's typically weighted less on the type system and more the fact that
I don't need to wait for a compiler or use any fancy tools to get a quick idea
realized.

~~~
stcredzero
If you remove a lot of the cruft and redundancy and add memory management, a
statically typed language with fast compiles can have some of the benefits of
a dynamic language.

If I could have a dynamic language, _and_ static typing, that would be the
best of both worlds. However, gradual typing doesn't seem to have caught on.

~~~
btilly
Gradual typing has a strong tendency to turn into never-typing because when
you've got it running there is no real point to going back and adding types.
Which divides people back into those who want dynamic typing and those who
want static typing.

~~~
stcredzero
Tracing JIT VMs gather the information on what the types actually are at
runtime. I wish that such JIT VMs running server software would gather such
information, which could then be automatically applied.

~~~
btilly
The JIT VMs gather that information, but then are careful to hide all of the
typed calls behind run-time checks for the validity of the types. Failing to
do so is an invitation to create bugs on rare use cases that didn't happen to
be in your run. Making those code changes either results in duplicated auto-
generated code that nobody looks at, or results in a source of possible bugs.

To give a concrete example, suppose that you have a weighted graph whose
weights are integers. Code to find a Minimum Spanning Tree will always have an
integer for the weight. Great. But if you add that type annotation, you'll run
into trouble when someone checks whether the previously found MST is unique by
calling the same algorithm again using as weights (weight,
edges_in_common_with_prev_mst). (This will find the MST that shares as few
edges as possible with the original, so if there are > 1 you'll find a
different one.) Those ordered pairs were just fine in the original code, but
don't fit the type annotation.

~~~
stcredzero
_The JIT VMs gather that information, but then are careful to hide all of the
typed calls behind run-time checks for the validity of the types. Failing to
do so is an invitation to create bugs on rare use cases that didn 't happen to
be in your run. Making those code changes either results in duplicated auto-
generated code that nobody looks at, or results in a source of possible bugs._

Of course, you want to use data correctly. There was a study of server code in
Python that found that lots of playing fast and loose with type information
happens just after startup, but then types settle down. If the initialization
can be isolated in certain parts of the code base, it would make such date
easier to use.

------
jmpeax
I find it frustrating how he's against "types" but shows the GCD algorithm
where x and y must adhere to a type that can be compared with equality, admits
an ordering, and has a subtraction operator. Is the algorithm defined for the
reals? Can we use a partial ordering? I'm skeptical of any proposal that seems
to have missed even a superficial coverage of existing knowledge/literature on
the topic.

------
scentoni
I noted that the one question in the video was asked by
[https://en.wikipedia.org/wiki/William_Kahan](https://en.wikipedia.org/wiki/William_Kahan)
, the main architect of
[https://en.wikipedia.org/wiki/IEEE_754](https://en.wikipedia.org/wiki/IEEE_754)
floating point. Both Lamport and Kahan are Turing Award winners.
[https://en.wikipedia.org/wiki/Leslie_Lamport](https://en.wikipedia.org/wiki/Leslie_Lamport)

------
cwyers
The assertion here seems to be that TLA+ isn't a programming language? I'm not
sure that makes sense to me.

~~~
pron
It most certainly isn't. It is generally impossible to interpret/compile a
TLA+ algorithm efficiently (i.e. as a program that runs with similar
complexity to the algorithm) because of the inherent nondeterminism in TLA+.
OTOH, it is precisely this nondeterminism that allows you to use TLA+ to
describe any algorithm/system at _any_ level of detail. In fact, you can show
that a language with similar descriptive power to TLA+ cannot be a programming
language, and that no programming language can have a similar descriptive
power (you could embed a specification language inside a programming language,
but then you arguably have two languages).

~~~
cwyers
Since when is it a requirement that a programming language be efficient?

My larger point is that he seems to be using "programming language" as a
shorthand for an imperative, typed programming language. A lot of what he says
doesn't seem to apply very well to SQL or Prolog or Lisp, for instance. (I say
this having only skimmed the slides, I don't have two hours for this right
now.) I won't say that it's a straw man, because there are plenty of
programming languages that fit the way he's using the term and some of them
are incredibly popular, but it seems to be a definition that leaves out a lot
of programming languages.

~~~
pron
> Since when is it a requirement that a programming language be efficient?

It is (generally) a requirement that if you're writing a linear time
algorithm, then the compiled/interpreted language will be executed in linear
time. This is not the case for TLA+. You can describe linear time algorithms
in a way that can only be compiled into, say, an exponential-time program, and
possibly even not at all (i.e., extracting an implementation is undecidable).

I wrote about this at some length here:
[https://pron.github.io/posts/tlaplus_part3](https://pron.github.io/posts/tlaplus_part3)

> My larger point is that he seems to be using "programming language" as a
> shorthand for an imperative, typed programming language

He means a language with the property I mentioned above. It is somewhat true
that Prolog has a tiny portion of the expressive power that comes from a truly
nondeterministic language such as TLA+, but it still falls under "programming
language." If you want to get technical, a language such as Prolog can let you
program with some limited use of quantifiers. In TLA+, you can specify using
unlimited use of quantifiers ranging over infinite and even uncountable
domains.

In TLA+ you can describe the property "decides halting in linear time," and
then show that a specific implementation can do so in some special cases for
example. No programming language can do that (i.e. describe non-computable
"algorithms") but it is _essential_ for specification languages.

~~~
Jweb_Guru
> No programming language can do that (i.e. describe non-computable
> "algorithms")

Coq certainly can describe properties like this; they're just uninhabited
types (all the necessary concepts needed to define "decides halting in linear
time" can be defined over an inductive definition of the step function for the
programming language of the halting decider; or if what you in fact meant was
"a type that represents programs that halt in linear time", it's even easier
and requires the same concepts, but a less powerful programming language).
There is no specification sublanguage required. I find your distinction to be
pretty iffy. Of course, Coq is not a very traditional programming language,
but it is a programming language nonetheless.

(I note that you claim that Coq separates types and terms into two different
semantic and syntactic categories, which isn't really true. The set model,
which is the standard model that justifies the consistency of the calculus of
inductive constructions, makes no such distinction!).

~~~
pron
Typed programming languages (any typed formal systems, for that matter) are
made of two languages, type level and term/object level. While the two are
strongly related, they are separate (a term can inhabit a type, but not
another term). The level of nondeterminism required for specification exists
at the type level and not at the term level, while programming (in the sense I
defined above) exists only at the term level. This is what I meant when I said
above that a specification language can be embedded into a programming
language (this doesn't require types, but can also be done with contracts),
but those are still two languages, even if they share syntax. If you like, you
can think of TLA+ as being entirely at the type level.

~~~
Jweb_Guru
My point is that there are models of (essentially) Coq where there is no
distinction between types and terms; both are translated into sets, in a
fairly straightforward way. Additionally, one of the major points of dependent
type theory is that reduction can occur anywhere, including directly within a
type; i.e. term and type cannot be defined separately from one another. I'll
go further and say that the fact that a term can only inhabit a type is in
some sense definitional, since types are also terms; I can quite easily
perform reduction and programming in a term that includes quantifiers, which
you would say was part of the nondeterministic type level (in other words,
terms _do_ inhabit other terms). So it is extremely unclear to me that the
distinction you are making is fundamental.

(To be clear, I do agree that there is a difference between specifying a type
and synthesizing an inhabitant for that type; I just think that the
"programming-specification" distinction you are making is somewhat artificial,
with dependently typed languages demonstrating this particularly effectively
by letting you program within your specification as well).

~~~
pron
> My point is that there are models of (essentially) Coq where there is no
> distinction between types and terms

I don't think this is true for Coq. Ultimately, Coq relies on a
type/inhabitant separation. When you spoke of Coq as a programming language,
the terms you can always "run" are only the inhabitants.

> So it is extremely unclear to me that the distinction you are making is
> fundamental.

I don't know if it is entirely binary, but it is sufficiently precise for
useful terminology. A programming language is a language that can be used for
programming, namely it is a language whose every term can be mechanically
interpreted in an "efficient manner."

> dependently typed languages demonstrating this particularly effectively by
> letting you program within your specification as well

I don't think that any existing dependently typed languages do anything
particularly effectively (neither programming nor specification), but that is
beside the point :) It is certainly possible to combine a specification
language and a programming language, but not without some formal delineation
or the experience would be frustrating (i.e., it is possible to interpret some
subset of TLA+ terms, but I am not sure it can be syntactically easy to
determine which). This is, however, something that TLA+ tries hard to avoid as
a design principle, because we do not yet know of any feasible way to scale
specification from the "program level" to arbitrary levels of interest. Unlike
Coq, TLA+ was designed not as a research tool for logicians, but as an
industrial-strength tool for practitioners (I wrote about the design
principles behind TLA+ in part 1 of my series:
[https://pron.github.io/tlaplus](https://pron.github.io/tlaplus)).

~~~
Jweb_Guru
> I don't think this is true for Coq. Ultimately, Coq relies on a
> type/inhabitant separation. When you spoke of Coq as a programming language,
> the terms you can always "run" are only the inhabitants.

Firstly, yes, it is absolutely true for Coq (or at least, a large subset of
it). The set model is essentially the entire basis for CiC's claim to
consistency. I'm fairly sure TLA+ also has such a model (somewhere).

Secondly, _all_ terms in Coq, including types and universes, are inhabitants
of some type, so there is no distinction between "the inhabitants" and all Coq
terms (it's true that weak head reduction leaves products and other obvious
types alone, but that's not the case for stronger forms of reduction like
strong normalization, or for full conversion). Moreover, the conversion that
occurs in the static phase (typechecking) has the same semantics as the
execution during the dynamic phase (runtime). So I really don't understand
what you're driving at.

> I don't know if it is entirely binary, but it is sufficiently precise for
> useful terminology. A programming language is a language that can be used
> for programming, namely it is a language whose every term can be
> mechanically interpreted in an "efficient manner."

"Efficient manner" is pretty vague. I suspect there are widely used
programming languages that will differ by 3 or 4 orders of magnitude while
evaluating semantically identical expressions, while nonetheless both clearly
being programming languages. Besides, many optimizations are only
optimizations for certain data distributions, and if the compiler guesses
wrong it can hurt performance; but that doesn't seem to me to have anything to
do with whether something is a programming language or not. If all you care
about is the default reduction, Coq's certainly tries to be fairly efficient:
beyond optimizing the usual lazy reduction, it exposes many other reduction
strategies, tuning knobs to tweak how reduction runs to improve performance
when domain information is available, and even contains multiple
implementations of some reduction strategies so people can select the
appropriate one for their use case.

> I don't think that any existing dependently typed languages do anything
> particularly effectively (neither programming nor specification), but that
> is beside the point :)

I'm well aware of your biases here. Regardless of their origins in PL
research, plenty of useful work has been done in dependently typed languages,
including much larger developments than any I'm aware of in TLA+. I'm not
really interested in arguing their relative merits--clearly, both are useful--
I'm just asking you not to pretend to make general statements about
programming languages while actually talking about TLA+.

~~~
pron
> The set model is essentially the entire basis for CiC's claim to
> consistency.

I am not saying that types do not have a set model, but that types and
inhabitants are fundamentally separate. Or, put another way, a lambda term
specifies a single (parameterized) execution (up to evaluation strategy), and
that is the _only_ thing that can be made to "run" (I'm sure there are ways to
extract, say, a sole inhabitant of a type, but types cannot _in general_ be
turned into executable programs).

> "Efficient manner" is pretty vague. I suspect there are widely used
> programming languages that will differ by 3 or 4 orders of magnitude while
> evaluating semantically identical expressions, while nonetheless both
> clearly being programming languages.

I'm talking about exponential differences in time complexity, or even infinite
ones. You can easily specify "computations" with infinite (or even
uncountable) nondeterminism in TLA.

> I'm just asking you not to pretend to make general statements about
> programming languages while actually talking about TLA+.

My (and I think, Lamport's point) is that there is a fundamental difference
between a specification language and a programming language. It is certainly
possible to mesh the two and call the result "one language", but then it must
necessarily contain two deductive systems, and so I think the distinction can
still be made. Even if you object to this distinction, you would agree that
the "specification parts" of Coq (i.e. those with significant nondeterminism
and use of quantifiers) cannot be generally mechanically executed in an
efficient manner. So whether it's two languages or one, there is a fundamental
difference between those expressions that can be used for programming (little
nondeterminism/use of quantifiers) and those that are necessary for
specification (unrestricted nondeterminism/quantifiers).

~~~
Jweb_Guru
> I'm talking about exponential differences in time complexity, or even
> infinite ones. You can easily specify "computations" with infinite (or even
> uncountable) nondeterminism in TLA.

SQL joins are worst-case exponential in the number of relations joined, where
n = the total number of rows in the database (though there are many special
cases that can help reduce this). I believe many people nonetheless consider
SQL a programming language--with WITH RECURSIVE, it is Turing-complete. You
could probably talk me into believing it's more of a "specification language,"
but despite that people use it for performance-critical things (admittedly
usually in one of the special cases where it's better-than-exponential, but
such special cases exist for most intractable problems; it doesn't seem to me
that the nature of the language can be thought to fundamentally change
depending on the data set).

> Even if you object to this distinction, you would agree that the
> "specification parts" of Coq (i.e. those with significant nondeterminism and
> use of quantifiers) cannot be generally mechanically executed in an
> efficient manner.

I specifically object to this! Coq executes what you term the specification
parts in a very different way than trying to exhaustively enumerate solutions
--one that is actually quite efficient and can be mechanically executed.
Essentially, rather than trying to eliminate the quantifier, it tries reducing
the type, and the body bound by it. A key point is that this mechanism isn't
special-cased for the "specification" parts--it's exactly the same reduction
mechanism used everywhere in the program, and indeed it's very important for
conversion that reduction behave this way. Thus, you can indeed efficiently,
mechanically execute a type in Coq. Once it reaches normal form, of course,
you can't perform any more reduction--but from the Coq kernel's perspective,
up to eta conversion, types in normal form are _distinct_ from an enumeration
of all their inhabitants, meaning it is indeed doing the right thing here.

In other words: Coq does not allow you to only program with non-type terms,
and it doesn't make a clean distinction between inhabitants of types and types
themselves. Moreover, while for quantified parts this isn't usually _that_
interesting, for nondeterministic parts (such as match expressions on unknown
variables) it can actually be quite important; for example, dependent
elimination within the return type of a match clause can determine the index
of a value of an inductively defined type, thus actually affecting program
execution (and vice versa, of course; for instance, a type can be constructed
representing n iterated lambdas, where n is an unknown value that may either
be specified at runtime or at compile time. It is this latter case that gives
rise to techniques like proof by reflection that may approximate an exhaustive
search in order to produce a proof object--but they do so because the user
wrote a program specifically to do exhaustive search, not because there's some
exhaustive search mechanism built into the language).

~~~
pron
> I believe many people nonetheless consider SQL a programming language

Yes, but I don't think anyone considers a SQL join to be a description of a
linear-time algorithm.

> Coq executes what you term the specification parts in a very different way
> than trying to exhaustively enumerate solutions--one that is actually quite
> efficient and can be mechanically executed.

I never said you always need to exhaustively enumerate solutions.
Nevertheless, the problem of extracting a computation from a specification
(e.g., an inhabitant from a type) is undecidable in general[1]. Hence,
specifications cannot be generally compiled, therefore they are not
programming languages.

They could be a new kind of programming language, where some search algorithm
could sometimes find a computation, but that would be a "programming language"
in a completely different sense; Lamport does acknowledge that possibility (I
know some people are researching program generation, but the results so don't
look very promising, and in any event no one expects them to ever be more than
heuristic, even if they're ever effective).

[1]: I'm not sure what happens if you know the type is inhabited. I guess it
will be decidable in that case (just as finding a proof of a proposition
that's known to be a theorem), but the search is still intractable, and there
is no way the resulting computation would be efficient.

~~~
Jweb_Guru
> Yes, but I don't think anyone considers a SQL join to be a description of a
> linear-time algorithm.

This is the first mention either of us have had of "linear-time" outside of a
discussion about specifications. There aren't many programming languages that
only let you write linear-time algorithms, and I think that's very far from
most people's definition of a programming language. That being said, it's not
like you can't write linear-time algorithms in Coq; its reduction machine is
pretty efficient w.r.t algorithmic complexity (and it's likely to gain native
machine integers and arrays soon to bring the constant factor in line with
this). _Proving_ linear-time execution for a native Coq function is another
question entirely, of course, but you can always model a specific reduction
machine in Coq and analyze that instead.

> Hence, specifications cannot be generally compiled, therefore they are not
> programming languages

I _really_ don't agree with you on this. You have a fixed notion in your head
if what it means to compile a type (attempt to find an inhabitant), but that
is not how Coq interprets types at all; from its perspective, types are terms
of independent interest, and are moreover themselves inhabitants of some other
type. We don't expect a program to try to "find the inhabitant" of a term of
type boolean when it's compiled; we want to keep reducing the term until it's
in normal form (and, hopefully, our theory is consistent so it will be either
true or false). In Coq, the same sort of reasoning (more or less) applies to
types; it just keeps reducing it until it reaches normal form. There's no
special notion of "finding an inhabitant" as distinct from normal program
execution, which is exactly why I claim that Coq does not make the
"specification-program" distinction. I'm not sure if I can explain it any more
simply. I am not saying this is not a meaningful distinction _in TLA+_ , or
that it's not a real concept, but it's not the only way of thinking about
things.

> I'm not sure what happens if you know the type is inhabited. It might be
> decidable in that case (just as finding a proof of a proposition that's
> known to be a theorem), but the search is still intractable, and there is no
> way the resulting computation would be efficient.

Depends on the problem. In Coq, for decidable problems you can write a
decision procedure that is tailored to the individual type, having it produce
a proof of the proposition you desire as a byproduct (in fact, in general
producing such a procedure is how you prove that the problem is decidable!).
Sometimes executing it is tractable and sometimes it isn't, but I don't agree
that writing an algorithm to solve such a problem automatically transforms
your domain from "efficient, everyday programming" to "ineffecient,
specification-driven programming." Moreover, a lot of the time, there will be
an easy inefficient decision procedure and a much harder efficient one, but
given that they're both written in the same language and solve the same
problem it seems hard for me to imagine that one would be considered "ordinary
programming" and the other "program extraction" or whatever.

(In response to your [1], tactic languages like Coq's Ltac, much maligned as
they are, are basically an attempt at interactive program synthesis; people
rarely write complicated proof terms by hand. So I'd say these ideas have been
a success, though hardly an unqualified one. Of course, one can argue that
it's not "really" synthesis because you're not synthesizing the whole program
from nothing but the specification, but even in ordinary program synthesis
most successful solutions are based on sketching, where a partial program is
provided and the synthesis engine fills in the gaps).

~~~
pron
> This is the first mention either of us have had of "linear-time" outside of
> a discussion about specifications.

I mentioned it before, and my point isn't about linear time in particular.
_If_ you write a specification of an algorithm of time complexity T(n), the
expectation is that interpreting the algorithm would take p(T(n)), where p is
some polynomial (i.e. no exponential slowdown or worse). This is what I mean
by "efficient".

> but that is not how Coq interprets types at all; from its perspective, types
> are terms of independent interest, and are moreover themselves inhabitants
> of some other type.

That doesn't matter. I can specify a function by giving it a type -- say it's
a function from the even naturals to their Goldbach pairs. Turning this into a
program requires finding a lambda term that computes such a function.

> There's no special notion of "finding an inhabitant" as distinct from normal
> program execution

What I meant was that a type in Coq is not a computation (although it could be
the _result_ of a computation), and a computation is not a type (although it
could _produce_ a type -- Pi types).

> I am not saying this is not a meaningful distinction in TLA+

In TLA+ there is no such distinction because you cannot talk about a program
that computes that function, but always the set of all programs that do (you
can, of course, give more and more detail to restrict the set, i.e., create
subtypes, but there is no " _the_ computation"); that set is always either
empty or infinite. In fact, if it's non-empty, it's always too big to even be
a set (it's a proper class).

> Sometimes executing it is tractable and sometimes it isn't, but I don't
> agree that writing an algorithm to solve such a problem automatically
> transforms your domain from "efficient, everyday programming" to
> "ineffecient, specification-driven programming."

I am not talking about writing an algorithm to solve such a problem
automatically, but about a programming language that _always_ does so.

> So I'd say these ideas have been a success, though hardly an unqualified
> one.

They have been an abysmal failure when it comes to synthesizing anything that
could be considered a replacement for programming. But we're talking about two
radically different measures of success. I'm talking about a commercial
process of turning lead into gold, and you're talking about turning a couple
of lead atoms into gold atoms in a particle accelerator.

~~~
Jweb_Guru
> No, I mentioned it before, and my point isn't about linear time in
> particular. If you write a specification of an algorithm of time complexity
> T(n), the expectation is that interpreting the algorithm would take O(T(n)).
> This is what I mean by "efficient".

 _What_ algorithm? There are many algorithms that inhabit the same type. A
specification that's detailed enough to be able to analyze its complexity is
usually a program... and it's easy to write a program that runs in linear time
in Coq.

> That doesn't matter. I can specify a function by giving it a type -- say
> it's a function from the naturals to their Goldbach pairs. Turning this into
> a program requires finding a lambda term that computes such a function.

In Coq, you cannot specify a function by giving its type. First, CiC is not an
extensional type theory in general; it cares about how things are computed,
not just what the answer is. Secondly, for many interesting function types in
Coq, there are an infinite, or even uncountable, number of inhabitants (for
example : Prop -> bool). Moreover, in Coq's core type theory, even if it could
automatically find _all_ lambda input-outputs pairs that inhabited some type,
that would not be justification to replace the quantified type (you need to
assume the function extensionality axiom if you want this).

> I didn't say there was. What I said was that a type in Coq is not a
> computation, and a computation is not a type. Just as a proof is not a
> proposition and a proposition is not a proof.

A type in Coq is no more or less a computation than a term is. I don't
understand what this has to do with "specification vs programming." As for "a
proposition is not a proof", I think that is the main point of significant
disagreement between us. Why do you think True doesn't prove Prop, for
instance? It may not be the most interesting proof in the world, but neither
is the proof I of True (and in many cases, proofs of Prop are _very_
interesting in themselves, particularly in the context of dependent
elimination).

> In TLA+ there is no such distinction. You cannot talk about a program that
> computes that function, but always the set of all programs that do (i.e.,
> just the type).

Okay, then neither of them have such a distinction. What you seem to be
missing is that in Coq, the mechanism for manipulating types is not different
from the mechanism for ordinary program evaluation; it looks like TLA+ uses an
axiomatic formulation rather than computation rules, so if you were to add
evaluation to TLA+ you'd probably need a new set of semantics, but that's not
the case for Coq.

> I am not talking about writing an algorithm to solve such a problem
> automatically, but about a programming language that always does so.

As you noted, such a programming language is impossible, so I don't understand
why you are talking about it. The impossibility of that hypothetical
programming language doesn't mean you can't write a _single_ unified
programming language (without two different "parts") that also functions as a
specification language, and I genuinely don't understand why you think it
does.

> They have been an abysmal failure when it comes to synthesizing anything
> that could be considered a replacement for programming.

The state of this has been steadily improving! For example,
[https://www.cs.utexas.edu/~isil/sypet-
popl17.pdf](https://www.cs.utexas.edu/~isil/sypet-popl17.pdf) looks quite
promising for everyday programming tasks.

~~~
pron
> A specification that's detailed enough to be able to analyze its complexity
> is usually a program...

Not necessarily, and I would claim that it _usually_ isn't, but to see that
you have to pay close attention to how algorithms and systems are specified. I
give the example of the QuickSort algorithm at the beginning of this post:
[https://pron.github.io/posts/tlaplus_part3](https://pron.github.io/posts/tlaplus_part3)

> In Coq, you cannot specify a function by giving its type.

Of course you can; that's the meaning of the type -- it's just not enough to
get a computation. That's _precisely_ the point I was getting at. A
specification is a description of something at some arbitrary level of detail,
but languages that allow compilation -- i.e. programming language -- normally
require a minimum level of detail for that to be possible. In a language like
Coq, this requirement is a strict syntactic distinction between types and
lambda terms.

> First, CiC is not an extensional type theory in general; it cares about how
> things are computed, not just what the answer is.

Precisely. This is absolutely necessary for a programming language, and
undesired in a specification language. (BTW, in TLA, what and how is
arbitrary, because the temporal logic lifts everything that in Coq is
operational into the denotation)

> A type in Coq is no more or less a computation than a term is.

A type is not a computation. To "run" something, or to speak of the operation
of something, that something must be a lambda term.

> the mechanism for manipulating types is not different from the mechanism for
> ordinary program evaluation

I know that (I don't know Coq, but I know a bit of Lean, which is similar, I
think), but that doesn't matter. You can compute _with_ types, but there is a
strict distinction between the common form of nonderterministic specification
-- types -- and actual computation ( _not_ types).

> As you noted, such a programming language is impossible, so I don't
> understand why you are talking about it.

Because we're trying to work out the difference between a specification
language and a programming language. A programming language is one that, when
it describes a computation, can _always_ be interpreted efficiently in the
sense I meant. A specification language should allow specifying computations
at arbitrary levels of detail. I claim that a specification language cannot be
a programming language and vice versa -- _except_ if we had this magical
language I described. What you _can_ do, and Coq does, is combine _two_
languages in one, but even then the result requires a delineation between a
computation and a specification (arbitrary nondet).

> For example, [https://www.cs.utexas.edu/~isil/sypet-
> popl17.pdf](https://www.cs.utexas.edu/~isil/sypet-popl17.pdf) looks quite
> promising for everyday programming tasks.

I honestly do like the research, but this is going from converting two atoms
of lead into gold in a particle accelarator to five, or like saying that
Olympic high jumpers are steadily improving in jumping all the way to the
moon... This is another discussion, but I like the research and I like the
practice, but I don't buy the claims that such improvements in research put
changes to practice on the horizon. The gap is still huge, and I also think
some of the research (especially in programming language theory) is done based
on assumptions that completely fail in practice (I wrote about that here:
[https://pron.github.io/posts/people-dont-write-
programs](https://pron.github.io/posts/people-dont-write-programs))

------
cttet
Don't have the chance to watch the video but highly disagree with the title.

Math as a language is just a DSL which carries a lot of historical burden that
is not friendly at all. Programming languages can achieve same level of
abstraction with more clarity. Math instead added too much specific syntactic
sugar(bad is multiply(b, a, d), log a is log(10, a) or log(e, a)), operator
overload (/ for divide and dy/dx for differentiation operator, same grammar
for scalar and matrix multiplication, one of which is communicative while the
other is not), and complicated design (integral would be much clearer if we
write integrate(target=ax, over=x, from=0, to=10) instead of the original
one).

~~~
wodenokoto
The talk is about TLA+, which is somehow not a programming language, yet you
follow a syntax and describe algorithms and then I'm not sure what happens.

Maybe you prove your algorithms and call it a day, or maybe you can ask your
program to run it, like an external dependency.

It's very unclear how TLA+ is neither programming or how it fits in with your
programming. But he is not asking you to do normal math notation.

~~~
pron
> Maybe you prove your algorithms and call it a day

You can do that, or check it with a model checker (which is much easier).
Either way, you _verify_ your algorithm rather than "run" it (although the
model checker lets you simulate or "run" a random execution).

------
dchichkov
I thought more progressive approach is phasing out incomplete and obscure
Greek single letter notations. Like done by OpenAI, or by "Structure and
Interpretation of Classical Mechanics" or in the "Functional Differential
Geometry".

------
untangle
A Programming Language (APL) started life as a mathematical notation. As such,
it is the closest thing that I have encountered for expressing algorithms as
code. It is the embodiment of "freeing the hidden algorithm." Fifty examples
of such are given in the following link:

[http://www.jsoftware.com/papers/50/](http://www.jsoftware.com/papers/50/)

------
ryanmarsh
With all due respect to Leslie Lamport his "what could be more beautiful"
explanation of Euclid's algorithm is no easier for me to make sense of than
these[0] various implementations.

For a "complex" problem[1] you cannot write a unified field definition[2]
before the fact. Otherwise the problem would have to be merely complicated
(most sufficiently useful systems are complex). The easiest way humans can
make sense of a complex problem is to define a set of misfits[2], or tests. In
practice efforts to find a better way of expressing the hidden algorithm of
any given software system have failed. Yet "tests" have proven themselves as a
sustainable way for humans to iteratively arrive at the most suitable solution
to a complex problem and to guide understanding after the fact.

tl;dr a more elegant programming language is nice but does not solve the
fundamental problem with human minds and solving complex problem domains

0:
[http://www.codecodex.com/wiki/Euclidean_algorithm](http://www.codecodex.com/wiki/Euclidean_algorithm)

1: Cynefin, Dave Snowden

2: Notes on the Synthesis of Form, Christopher Alexander

~~~
gav
I have a feeling if more people read Alexander's books on design and patterns
the quality of systems as a whole would increase. He is largely unknown in the
tech circles I've been in.

------
dzdt
Lamport is famous in part for his work on LaTeX, the document typesetting
language. It is Turing complete. Should no one write papers in LaTeX?

~~~
tincholio
I'd say that he's known as one of the fathers of distributed systems, and for
winning a Turing award, more than for writing LaTeX. Also, it's not LaTeX
that's Turing complete, but rather TeX. LaTeX is basically a bunch of TeX
macros.

Also, the rest of your comment doesn't make any sense, either.

~~~
pessimizer
It's not a confusing comment. Lamport says that programming languages should
only be used for programs. Lamport is also famous for using a programming
language for typesetting.

A comment about the things you think he's more famous for doesn't make sense.
A comment about LaTeX not being turing-complete, and citing as proof that it's
largely a bunch of macros written in a turing-complete language (glued
together with that turing-complete language) doesn't make sense either.

------
fastball
As a side note: this presentation has a lot of wasted space in the video, and
no offense to the speaker but I'd rather have a better view of his slides than
his face. I can't even read some of the algorithms put on the screen!

~~~
yoz-y
I agree, the slides and speaker views should have been flipped.

