News for hackers. Stuff that matters.
Re:Code is Engineering; Types are Science by dwohnitmok on Monday April 06, 2020 @10:59AM (#22794303)
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.

Re:Code is Engineering; Types are Science by UncleMeat on Monday April 06, 2020 @11:02AM (#22794334)
Casting is not always permitted in all contexts. In C++, many casts that would be valid on the real machine are undefined behavior in the language, for example.
Re:Code is Engineering; Types are Science by dwohnitmok on Monday April 06, 2020 @11:14AM (#22794449)
That's sort of the point though. Contexts where casting is not permitted in that sense are where the types are an integral part of the semantics of the language and for which it doesn't make sense to speak of "otherwise valid statements" (as indeed is the case here with C++).

The original comment's "otherwise valid statements" are precisely UB in C++.

Re:Code is Engineering; Types are Science by UncleMeat on Monday April 06, 2020 @11:38AM (#22794642)
But is there a difference between a statement that is excluded from the language by a non-typing rule and a statement that is excluded from the language by a typing rule? In both cases, the "natural" evaluation could work as the developer intends but some language rule prevents that program from being executed.
Re:Code is Engineering; Types are Science by dwohnitmok on Monday April 06, 2020 @12:11PM (#22794925)
Honestly the answer is it depends and needs to be looked at on a language-by-language basis. There's certain languages for which there is no difference between typing rules and non-typing rules. Type-checking is literally the same as syntax checking (this is what I was referring to with a types as macros approach that has an implementation in Racket).

There's others where you can try to finesse some sort of Curry-style semantics out of a language that doesn't have those semantics.

The crucial question though is to what end?

The original contention is that types represent a limitation on a language, a limitation that you might want to sometimes lift. To the extent that you can separate the types from a language itself I agree that's true in theory, but that problem is solvable with casts.

And to the extent it isn't solvable with casts, it isn't solvable precisely because the Curry-style interpretation fails and the line between being excluded by a "surface-level" typing rule vs a deeper matter of language semantics is blurred.

All that being said, there is an argument to be made that you sometimes want to preserve a Curry-style fragment of your language precisely in order to facilitate this kind of "escape hatch" with a cast

However, going back to my original point, at least for me, this isn't something I worry about with mainstream languages. It feels more like a theoretical concern than a practical one. If you have a language with a type system that cares a lot about soundness and expressivity then it's something to keep in mind. I worry in dependently-typed languages that without preserving this distinction between types that have runtime-significance and types that do not, you lack an effective cast escape hatch.

For mainstream languages my concerns are exactly the opposite. The type system often is of little use to me because so much code makes use of escape hatches.

Re:Code is Engineering; Types are Science by dwohnitmok on Monday April 06, 2020 @11:47AM (#22794726)
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.

Re:Code is Engineering; Types are Science by thekhatribharat on Monday April 06, 2020 @04:51PM (#22797723)
> There are effectively two separate languages. Your type-level language and your term-level language.

Computational Type Theory (CTT) blends the distinction b/w terms and types, and in fact C-H correspondence bears no consequence on CTT research.

From a previous comment [1]:

> I worry in dependently-typed languages that without preserving this distinction between types that have runtime-significance (Church-style) and types that do not (Curry-style), you lack an effective cast escape hatch.

I think we've basically two categories of (turing-complete) programming languages:

1. Mainstream languages that use type systems for multiple purposes: operational semantics, compiler optimization, lightweight formal verification, etc, and provide casting escape hatches.

2. Languages where the type system's primary (and only?) purpose is formal verification, and there are no escape hatches.

It's just that 1 and 2 are really meant for different domains. 2 is mostly used for high-assurance domains like aerospace, automotive, healthcare, blockchain smart contracts where formal verification is high priority and escape hatches aren't sought, hence not built-in.

[1] https://news.ycombinator.com/item?id=22794925

Re:Code is Engineering; Types are Science by somewhereoutth on Monday April 06, 2020 @07:29PM (#22798967)
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!

Re:Code is Engineering; Types are Science by dwohnitmok on Tuesday April 07, 2020 @10:58AM (#22803799)
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.

Re:Code is Engineering; Types are Science by somewhereoutth on Tuesday April 07, 2020 @07:05PM (#22808428)
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.

Re:Code is Engineering; Types are Science by somewhereoutth on Tuesday April 07, 2020 @07:25PM (#22808566)
(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.

Re:Code is Engineering; Types are Science by dwohnitmok on Tuesday April 07, 2020 @09:41PM (#22809313)
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.