
Code is Engineering; Types are Science - behnamoh
https://www.tweag.io/posts/2020-03-05-peirce.html
======
Rochus
_ISO /IEC/IEEE 24765

3.3810 software engineering

1\. systematic application of scientific and technological knowledge, methods,
and experience to the design, implementation, testing, and documentation of
software ..._

It's all there just waiting for someone to read it.

~~~
eternalban
At CHF 198.00, IEEE is "just" waiting to collect fees before permitting
unwashed practitioners from reading it.

But we do get a preview and there is a link to www.computer.org/sevocab so we
can get a sense of their taxonomical approach. (Try searching for "Data
type").

~~~
Rochus
No, it's free. Just download it from
[https://standards.iso.org/ittf/PubliclyAvailableStandards/c0...](https://standards.iso.org/ittf/PubliclyAvailableStandards/c071952_ISO_IEC_IEEE_24765_2017.zip).

~~~
eternalban
Thanks!

------
somewhereoutth
Following Godel:- my intuition suggests to me that for a sufficiently powerful
programming language (Turing complete?), _any_ type system will preclude
otherwise valid statements in the language. Such illegal but valid statements
will be much more numerous than legal statements. These statements may not
just be valid, they may be useful, and perhaps even elegant!

~~~
dwohnitmok
I've never really bought this argument. Casting is the way a programmer can
selectively bypass the type system and every typed language I've ever used
includes casting.

For Church-typed languages as opposed to Curry-typed languages, it's not even
apparent what it would would mean to "preclude otherwise valid statements"
because types are a part of the language. It's akin to saying structured
programming precludes otherwise valid statements in the language because the
lack of goto precludes otherwise valid statements in the language. Sure, but
the lack of goto is precisely a facet of the language! For a Church-typed
language the types are just another part of syntax (this is more than just
theory, Racket shows that a type system can in fact be implemented simply as
macros).

Invoking Godel here I don't think makes sense. Godel's incompleteness theorems
apply to what you can prove about a program with types. The majority of typed
programming languages have type systems that are too weak for Godel's
incompleteness theorems to apply. And there is no need for a type system to be
sound (if thought of as a logical system with programs as proofs via the
Curry-Howard isomorphism), again most programming languages' type systems are
not sound in this manner.

Even with a Curry-style system of semantics, the presence of casting is enough
to allow any type system to allow every valid statement in the language (the
proof of this is to cast every term to the same type).

That being said I am sympathetic to the feeling that "I'm just doing this to
satisfy the type checker," a feeling that can show up increasingly frequently
for languages with increasingly sophisticated type systems. I would argue this
often happens precisely when you take the power of casting away. This is the
dark side of "correct by construction" techniques where if the construction
technique is devilishly complex, then you're forced to deal with that
complexity. But this usually only applies to languages with some amount of
dependent typing, which the majority of languages do not have.

~~~
dwohnitmok
I'll post a reply that I wrote to one of parent's comments that was deleted,
but I think the parent had some reasonable points.

It's slightly tweaked to remove some of the parts that don't really make sense
without the other comment's context.

You're implicitly assuming a Curry-style semantics to your language. Here's an
outline of what I assume you mean. Correct me if I'm wrong.

There are effectively two separate languages. Your type-level language and
your term-level language. Each of these languages have their own notions of
validity. At the type-level, a series of statements is valid if the type
checker accepts them. At the term-level a series of statements is valid if the
program runs without crashing. There is also some way of associating the two
languages together, i.e. type assignment.

Given these two languages, we would like for their two notions of validity to
coincide. That is it would be great if given a type assignment, the type-level
language is valid (type checker accepts it) if and only if the term-level
language is valid (program does not crash).

The first thing to note is that many (most?) statically typed languages don't
have Curry-style semantics. As UncleMeat points out not crashing is not
sufficient to be semantically valid in C++. It might just happen not to crash
on your machine with a certain set of optimizations and compiler flags. Types
play an integral role in what it means for a language to be semantically valid
and it doesn't really make sense to consider the language independently of
types.

For Curry-style languages, there is an indirect linkage to Godel, but the more
obvious link is through the halting problem. If type checking is decidable
(something you probably want to be true) then your term-level language must be
decidable as well if the two languages truly coincide. But then you lose
Turing completeness as your first comment points out.

The thing is though that no mainstream statically typed language tries to
ensure this. All of them allow assigning a type to something that might loop
forever or crash.

I would argue you're effectively attacking a straw man of a type system.

However, I agree with you that "there are times when a type system is too
cumbersome, and we must escape it to do useful things." However, this is not
something I view as limited to type systems. I view this as true of every
facet of programming (and in a greater sense true of every facet of life).

Every system must have a way of piercing its assumptions when you as its user
have extra knowledge the system does not. This shows up in performance and
protection against side-channel attacks (assembly intrinsics, FFI, etc.). This
shows up in orchestration tools (yes yes yes it's all good and well to treat
infrastructure as immutable and machines like cattle not pets but I would
sometimes like to just SSH onto a machine and make some one-off changes!).
This shows up in structured programming (once in a very blue moon Python, it
would be great if you just gave me a goto/label). The list goes on and on.

~~~
somewhereoutth
Thankyou for your reply (and apologies for deleting the comment).

I am not sure that the distinction between Curry and Church semantics is so
critical - after all we have but a single 'engine of computation': beta
reduction, applied to statements in the untyped lambda calculus. Thus a
statement with Church annotations will reduce exactly as one without?

I think I'm taking the view that statements in the untyped lambda calculus,
with beta reduction, are our objects of study, and a type system (Church or
Curry derived) is a set of theorems (or indeed a mechanism for generating
theorems) that we use to prove certain facts about such objects, again much
like the Peano axiomatization of natural numbers. Thus I profess my heresy!

~~~
dwohnitmok
This is not heresy; it's just Curry. The assertion that beta reduction (i.e.
evaluation) does not rely on type information and types are overlaid on top is
precisely the heart of Curry semantics.

The distinction between Curry and Church semantics does not seem particularly
relevant for something like the simply typed lambda calculus.

After all

    
    
        f : Int -> Int
        f(x) = x + 1
    
        f(5) : Int // 6
    

can simply have its types removed after type checking and everything will
still run right?

    
    
        f(x) = x + 1
    
        f(5) // 6
    

However this distinction becomes more important for type systems with
polymorphism, particularly ad-hoc polymorphism.

    
    
        f : a -> b where b is Zeroable
        f(x) = zero
    
        // E.g. List[a] is Zeroable where zero: List[a] = emptyList
        // Int is Zeroable where zero: Int = 0
        // String is Zeroable where zero: String = ""
    

Now an expression such as f(5) is impossible to evaluate without type
information.

    
    
        f(5): List[Boolean] // This is emptyList
        f(5): Int // This is 0
        f(5): String // This is ""
    

Types become an essential part of what it means to evaluate a term and hence
we are led to Church semantics.

Now there are ways of trying to recover Curry-style semantics. One way is to
replicate the type system at runtime. Then of course you're free to "erase"
the types since you still have them at runtime anyways!

This is essentially what statically typed object-oriented languages do with
late binding. _Note that in the extreme though, the type system cannot limit
the scope of valid programs because valid programs at runtime are precisely
those valid at type-checking time because the types are replicated!_ Often
though only fragments of the type system will be preserved at runtime so that
there is still a discrepancy. For example, usually return type polymorphism
(the previous example of f) is disallowed in these systems (see e.g. Java) to
avoid having to embed even more of the type system than just the types of
individual objects.

Another option is to compile the program down into a language with a simpler
type system (or no type system at all!) and then use Curry-style semantics
with the simpler type system. However, that feels more like an implementation
detail than anything else. Just because I can compile Java to C does not mean
that they have the same semantics. Indeed the point of compilation is
precisely to translate between two systems with incompatible semantics,
otherwise the compilation is trivial.

Stepping back from all this, Curry-style vs Church-style semantics have
implications for type checking and type inference. In general things are
harder in a Curry-style regimen. Type-checking which is decidable in a Church
system often becomes undecidable in an equivalent Curry system. This makes
Curry-style semantics burdensome to work with in rich type systems.

Curry-style semantics are of course an ideal fit for gradual typing systems
that aim to go from an untyped system and gradually add typing on top of that
untyped system.

~~~
somewhereoutth
I'm sorry but this is wrong: "The assertion that beta reduction (i.e.
evaluation) does not rely on type information and types are overlaid on top is
precisely the heart of Curry semantics."

Beta reduction is __not __affected by types ascribed to terms. The crucial
difference between Church and Curry is that Curry allows each term to be
ascribed a _set_ of types - perhaps 0, 1 or many - whereas Church insists on
each term having exactly one (often annotated). That is all. Both are systems
for proving type correctness of terms, and are no more than that.

~~~
somewhereoutth
(cont'd) ... and I see it now, the damage that has been wrought.

I imagine Church never dreamed that his approach of unitary type assignment
would lead people to believe that types actually exist, and that programs
should do different things when meeting such 'types'.

Yes let's get back to Curry, and build a better world.

~~~
dwohnitmok
Ah indeed you're right about beta-reduction; jumping to equating it with
general evaluation is a bridge too far.

Eh... I dunno. Church was around for a long time. He saw a lot of the
languages we use now.

That ship for ad hoc polymorphism sailed a long time ago. Almost every widely
used language I can think of has ad hoc polymorphism, whether that be through
runtime types (late binding a la Python, PHP, Ruby, Common Lisp, Javascript,
Java, Clojure, Scala, C#, C++, Smalltalk) or through compile time translation
(early binding a la Rust, Haskell, also Scala, ML, also Java, also C++, Idris,
Agda, Coq). The only languages I can think of at the moment that doesn't have
ad hoc polymorphism are C and Elm.

To put it another way every language that supports some notion of overloading
has to do a different thing when meeting a type. And I think you'd have a hard
time taking overloading away from programmers.

------
taneq
If code is engineering, then types aren't science; they're electronic design
rules. Coding without types is like trying to design a printed circuit board
without a 'check widths and clearances' button.

~~~
nine_k
More like without "check for disconnected circuits" button.

~~~
taneq
That too. Usually your 'DRC' button will check for unconnected signals,
shorted signals, clearances, and a bunch of other stuff that I've never used.
Having the ability to enter a bit of metadata and then have the computer
automatically and almost infallibly check your work is one of the best bits of
CAD.

------
platz
The point of the article, for those who read it, is to characterize different
activities of development in a static language as deductive, abductive, or
inductive and to discuss examples and relationships between these activities.

it's not to contrast static languages with dynamic languages.

------
Pmop
Engineering can happen way before you do any coding. It just turns out that's
almost industry standard to do both at the same time these days.

------
ukj
Similar ideas come out of the NuPRL project (
[http://nuprl.org/Intro/intro.html](http://nuprl.org/Intro/intro.html) )

> Starting with the slogan "proofs-as-programs," we now talk about "theories-
> as-systems."

But if you rewind history, you could also see the parallels to Peter Naur's
"Programming as Theory building"

Or more recently: Luciano Floridi's "The Logic of Information: A Theory of
Philosophy as Conceptual Design"

Different descriptions of the same thing. You could probably relate most of
these ideas all the way back to the Greek classics. Democritus. Aristotle.
Socrates. Plato.

Our tools are finally catching up...

------
bgorman
Interesting article. Types definitely make it easier to figure how things work
in new codebases. I wonder how often do experienced Haskell developers find
bugs in production.

~~~
tsimionescu
> I wonder how often do experienced Haskell developers find bugs in production

If by this you mean that you are wondering how often bugs in Haskell programs
make it to production, the answer seems to be "at about the same rate as bugs
in C or Java, as far as we can tell". Which, given that Haskell's (default)
type system is not much more powerful at expressing constraints than C or
Java's, shouldn't be a big surprise.

Haskell's type system is much more flexible, allowing you to express specific
types for many cases where in Java or C you would have to resort to Object or
void*; but otherwise, Haskell types can not encode much more powerful
constraints than C or Java types - you can only express the available
operations and the "shape". The bulk of the power comes from enforcing purity,
but that is essentially a property of the standard library, not of the type
system. For example, you can't express that a type is a Monad in Haskell - you
can specify that it supports the Monad operations, but you have to rely on
manual checking to see if it respects all of the monad laws.

Note that Idris or Coq are another matter entirely (as are dependent type
systems implemented in Haskell extensions).

~~~
sweeneyrod
> If by this you mean that you are wondering how often bugs in Haskell
> programs make it to production, the answer seems to be "at about the same
> rate as bugs in C or Java, as far as we can tell".

That seems surprising. What are the bugs that Haskell has but C and Java don't
that replace all the null pointer exceptions?

~~~
danielscrubs
I had a horrible problem with space leaks when running weeks in production,
didn't help that I used a pure Haskell db at the time. :)

Here are some more info [http://blog.ezyang.com/2011/05/space-leak-
zoo/](http://blog.ezyang.com/2011/05/space-leak-zoo/)

It's been almost a decade since though. Maybe the compiler catches more
nowadays.

------
29athrowaway
Science -> Engineering -> Technology

Technology is applied engineering, and engineering is applied science.

The study of types may fall into some field of science. Applying it is
engineering.

~~~
tsimionescu
The study of types is generally done as part of mathematics, not sciences.
There are some attempts to study type system impact in real world programs,
and ergonomics, which would fall under science, but these are a very small
part of PLT.

However, the point of the article was different - the article was claiming
that when you are writing the actual code for your program, you are normally
using engineering-style reasoning (abduction), whereas when you are defining
the hierarchy of types for your system, you are mainly applying scientific-
style reasoning (induction).

Personally, I think that the article is a gross over-simplification of every
single term - of deduction, abduction, and induction; of maths, science, and
engineering; and of what programmers actually do. It is in fact so over-
simplified that I don't see any valuable insight into the article at all. It
almlst reads like a description of programming from a TV show that is trying
to sound brainy.

------
dadair_ca
Software development doesn’t need to be put into a singular box; it’s all of
engineering, art, science. It’s also none of them.

------
slifin
If I was given the choice, I can have either static analysis or types I would
pick static analysis every time

~~~
nicoburns
Types are just one kind of static analysis. So it makes sense that you'd want
the superset rather than the subset.

~~~
naasking
It's not so clear cut. Types are compositional, by definition.

Static analysis needn't be compositional, and so a small change could cause
dramatic and unpredictable long-range effects elsewhere in your code. Figuring
out what changed or what went wrong can be a nightmare.

------
devin
It’s not an either/or.

------
thekhatribharat
I put a related question on Computer Science Stack Exchange a week ago.
Linking it here in the hopes that HN users roll-calling here will shed some
light.

Ref: [https://cs.stackexchange.com/questions/122066/does-the-
under...](https://cs.stackexchange.com/questions/122066/does-the-underlying-
computational-calculus-in-type-theories-affect-decidability)

------
enriquto
Then, fortran or numpy programmers who can only conceive a single type of data
(the multi-dimensional array of floats), are not doing science? I disagree.

~~~
91edec
I prefer to call them guild navigators.

~~~
shepherdjerred
What's the spice in this analogy

~~~
thesuperbigfrog
Caffeine.

It is by will alone I set my mind in motion. It is by the brew of arabica that
thoughts acquire speed, the teeth acquire stains, stains become a warning. It
is by will alone I set my mind in motion.

------
seemslegit
Falsely dichotomic analogies are falsely dichotomic

------
kazinator
Types are Science. Static types are theoretical science. Dynamic types are
empirical science.

------
indymike
An elephant's toenail is part of the elephant, but is not "the elephant."
Types are implementation details to a computer scientist, just as unit
conversions are implementation details to a chemist. Likewise, code is an
artifact of engineering, not the engineering itself.

------
kazinator
Counterpoint: code is scripture; types are religion.

~~~
AnimalMuppet
That doesn't seem quite right to me. Maybe code is sermons, types are
scripture? Or even code is a religious person's lived-out life, and types are
scripture?

------
crimsonalucard
He conflates science and logic. Science and logic are two very different
things.

for example, given:

    
    
       f(x) = x*0;
    

verify the hypothesis that:

    
    
       for all n in Real Numbers, f(n) = 0
    

To verify the above using science you would take a statistical sample of x and
verify that it equals zero for that sample.

    
    
       1*0 == 0
       2*0 == 0
       3*0 == 0
       4*0 == 0
    

That's four test samples out of an infinite domain. To fully verify the
function f via science you technically need infinite test cases to verify the
fact. Usually you can never achieve this in reality so science usually takes
what you call a "sample" and you say that if the sample is true very likely
the hypothesis is true. The only assumption science makes is that probability
theory applies to events in reality.

Note the isomorphism between science and unit testing; Same bs with none of
the statistical rigor.

To do the same in logic you have to make assumptions from axioms and logically
derive a proof from axioms.

The previous thing which was called a hypothesis is now called a theorem:

    
    
       for all n in Real Numbers, f(n) = 0  
    

The proof of the theorem from the axioms of arithmetic can be found here:
[https://math.stackexchange.com/questions/400605/a-proof-
of-n...](https://math.stackexchange.com/questions/400605/a-proof-of-n0-0)

That being said how does Type theory and code apply to science and logic and
engineering?

Type theory is part of logic it is not part of science. Don't conflate the
two.

What is engineering? Engineering is creating solutions for problems using
either or both Science/Logic. In the realm of software engineering there is
very little of both, It's all an art. Logic is rarely employed for
verification and when it's done it's usually just type checking; and testing
isn't done to a rigorous level that other scientific and engineering
disciplines require.

For say the creation of the 787 airliner. The entire plane is built
theoretically in software to such a degree that when they actually materialize
the theoretical model as a physical prototype it can actually fly. Then after
it is built they test it rigorously to a very high degree.

For most of software, none of the above is is ever done. We mostly work like
artists, just making shit up (design patterns) and adhoc automated testing.

So in all practicality your coding job has no relation to either science,
engineering or logic. You are an artist.

 _I know technically you apply bits an pieces of science and logic in your
programming job but ultimately you use adhoc logic and science for even
crossing the street, I 'm not going to count programming as a "science" due to
this._

~~~
eli_gottlieb
>That's four samples out of an infinite domain. To fully verify the function f
via science you technically need infinite test cases to verify the fact.
Usually you can never achieve this in reality so science usually takes what
you call a "sample" and you say that if the sample is true very likely the
hypothesis is true. The only assumption science makes is that probability
theory applies to events in reality.

Usually equating null-hypothesis significance testing with the scientific
method is a sign of cargo cult.

~~~
crimsonalucard
To verify an entire domain you need to test the full domain.

I'm not strictly equating inference or verification to science. I am simply
equating this:
[https://www.wikiwand.com/en/Scientific_method](https://www.wikiwand.com/en/Scientific_method)

Testing and hypothesis are the key words. Prediction is the other keyword.
Analysis I never covered, but you assumed I used null hypothesis testing.

Tell me what is your opinion of all of this since you think my opinion is a
'cargo cult.'

