Live data from Hacker News

Types

gist.github.com

101–110 of 198 posts

Re: Types

#101
post #85

Someone who has experience with both Agda and Idris, could you comment on which is easier to get started with? Coming from a Haskell background, I'd like to try my hand at writing more interesting constraints into my types. Any experiences using these languages for (non-research) work? I've played around with Coq a little and it certainly feels more like a proof assistant than a programming language. It was fairly co…

I found Idris pretty easy to get started with - that's part of it's goal, making dependent types easier to get started with. From there I think it would be easier to go to Agda or Coq, but I haven't explored much there myself.

+1 for Idris. The tutorials were pretty solid for it too when I tried it, and the mailing list was very helpful when I had questions [1]. There's even a WIP book! [2]

[1] https://groups.google.com/forum/#!forum/idris-lang

[2] https://www.manning.com/books/type-driven-development-with-i...

Re: Types

#102

Earlier quoted context omitted.

Why not just learn Coq or Agda yourself? There are also sorts of introductions for simple type systems that I'm sure you can build off of.

I have no idea how to implement fresh unification variable generation (my type system is a superset of Damas-Milner, kind of like how the calculus of constructions is a superset of System F-omega) in Coq or Agda. Everywhere I've seen it implemented, it involves mutable state. So, “in principle”, I could fake it using something like the state monad, and plumbing around the collection of unification variables generated…

You could take a look at Coquand (2002) “A formalised proof of the soundness and completeness of a simply typed lambda-calculus with explicit substitutions”.

https://www.dropbox.com/s/xmj7yv1i1moe0ag/Coquand2002.pdf

Drop in to #dependent on Freenode IRC, and let’s chat.

Re: Types

#103
post #85

Earlier quoted context omitted.

I found Idris pretty easy to get started with - that's part of it's goal, making dependent types easier to get started with. From there I think it would be easier to go to Agda or Coq, but I haven't explored much there myself.

+1 for Idris. The tutorials were pretty solid for it too when I tried it, and the mailing list was very helpful when I had questions [1]. There's even a WIP book! [2] [1] https://groups.google.com/forum/#!forum/idris-lang [2] https://www.manning.com/books/type-driven-development-with-i...

Their main docs are pretty good too: http://docs.idris-lang.org

Re: Types

#104

Earlier quoted context omitted.

Huh tactics are pretty great. Say exactly what you want and the computer programs itself! It's unfair to just compare development time between tactic-generated programs in a dependent language with manually written programs in a non-depenendent language. The end result in the dependent language is much more valuable.

I'm not comparing dependent types vs. no dependent types. I'm comparing higher-order dependent types (Agda, Idris, Coq, etc.) vs. first-order dependent types: https://news.ycombinator.com/item?id=12350147 Seriously, Coq-style proof scripts are utterly unredable when they grow past a certain size. The only way to understand them is to replay them, so that you can see the intermediate hypotheses and goals.

I agree. Note that the Coq-style tactics-based approach is not the only possibility.

In Agda, you are expected to write proofs in a functional style, similar to Haskell programs. There is a small amount of integrated automation, which helps you fill in holes in your proofs — however, the results are explicit proof terms, inserted at the right place in your program.

Systems which behave in this fashion have the de Bruijn criterion, as described in Geuvers (2009) “Proof assistants: History, ideas, and future”.

https://www.dropbox.com/s/4mwtxojg7yqb365/Geuvers2009.pdf

Re: Types

#105
post #20

1 + eval(read_from_the_network()) > If we get an integer, that expression is fine; if we get a string, it's not. We can't know what we'll get until we actually run, so we can't statically analyze the type. > The unsatisfying solution used in practice is to give eval() the type Any, which is like Object in some OO languages or interface {} in Go: it's the type that can have any value. Values of type Any aren't constra…

> if an expression evaluates without error, then its type is… What’s curious is that this is true in every language with general recursion/looping: int f(int n) { while (true) {} return n + 1; } (In other words, the type of “f” isn’t the logical formula “a → a”, but rather “a → ¬¬a”.) The type of “eval” can also be fully static; it can just return a type that you have to do case analysis or runtime type inspection on…

> The type of “eval” can also be fully static; it can just return a type that you have to do case analysis or runtime type inspection on to use.

Yes! The problem is, we don’t have a static type system which would allow us to intensionally analyse syntax at runtime. See section 9 of Sheard (2001) “Accomplishments and research challenges in meta-programming”.

https://www.dropbox.com/s/pathrmr7rufkw1d/Sheard2001.pdf

If you’re interested in this subject, perhaps drop in to #dependent on Freenode IRC, or ping me on Twitter.

Re: Types

#106
post #72

Earlier quoted context omitted.

What would happen if you did not compare them before calling the function, and just passed them in such that x >= y?

Nothing; it would return a partially applied function that waits for a proof that `x The key here is that `add` is not a function `Nat -> Nat -> Nat`, it is a function `(a : Nat) -> (b : Nat) -> LT a b -> Nat`. There are just some compiler features that allow you to avoid having to write out the full `LT a b` proof each time you want to add things!

What's even more interesting is the structure of "LT a b". Let's assume that Nat has the common Peano arithmetic structure:

    data Nat : Type where
      Zero : Nat
      Succ : Nat -> Nat
Here "Zero" represents zero and "Succ" represents "one more than". Hence "Succ (Succ (Succ Zero))" is one more than one more than one more than zero, AKA three.

Given two such values "x" and "y", how on Earth can we prove that "x Well, there's a really obvious case: we know that "x

    Obv : (x : Nat) -> LT x (Succ x)
Notice that we don't write "y" explicitly, since it can be written in terms of "x".

What about those cases where "x" and "y" differ by some other amount? We could define values for "x

    Ind : (x : Nat) -> (y : Nat) -> LT x y -> LT x (Succ y)
This is enough to prove that "x" is less than any number greater than "x". In fact, we don't need to give the values of "x" and "y" explicitly, as they appear in the "LT x y" types and can hence be inferred. In languages like Idris we indicate inferrable parameters using braces, hence our "LT" type looks something like this:

    data LT : Nat -> Nat -> Type where
      Obv : {x : Nat} -> LT x (Succ x)
      Ind : {x : Nat} -> {y : Nat} -> LT x y -> LT x (Succ y)
Now, this looks familiar. Compare it to the definition of "Nat": we have two constructors, one of which ("Zero"/"Obv") can be written on its own, whilst the other ("Succ"/"Ind") is recursive, requiring an argument containing the same constructors.

Values of type Nat include:

               Zero  : Nat
          Succ Zero  : Nat
    Succ (Succ Zero) : Nat
And so on, whilst values of "LT x y" include:

             Obv  : LT x (Succ x)
         Ind Obv  : LT x (Succ (Succ x))
    Ind (Ind Obv) : LT x (Succ (Succ (Succ x)))
Although LT contains more static information than Nat, it actually follows exactly the same structure. What does this mean? Values of type "LT x y" are numbers; in particular they're the difference between "x" and "y"!

In the case of "LT", these numbers start counting from one (since one number is not less than another if their difference is zero). If we define a similar type for "x " and ">=".

These types are actually really useful. They're also closely related to linked lists, vectors, etc. although they store dynamic information as well as static.

I assume this is all old hat to those with mathematical training, but I found it interesting enough to write about at http://chriswarbo.net/blog/2014-12-04-Nat_like_types.html

Re: Types

#107

Earlier quoted context omitted.

>You don't get this for free and you have to help the computer verify these. For library types, the most commonly used properties will have already been proven; you wouldn't have to write a proof that "sort" sorts a list any more than you'd have to write the sort function itself. The computer can also be surprisingly good at proving these things in many cases. E.g. I was playing with the proof assistant Isabelle rece…

> For library types, the most commonly used properties will have already been proven; you wouldn't have to write a proof that "sort" sorts a list any more than you'd have to write the sort function itself. Sorting was just an example. Most proofs of nontrivial algorithms come with reams of hand written proofs. Happy to see examples that go against this though. > The computer can also be surprisingly good at proving t…

You don't have to write a proof if you don't want to. It's quite possible to simply write your sorting function without a proof, it's just that at that point you don't know that your function works as advertised (which of course is then just the same thing that you get in a non-dependently-typed language).

    data Comp = Gt | Lt | Eq

    sortBy : (a -> a -> Comp) -> List a -> List a
    sortBy = ...
The above is perfectly acceptable in Idris, given that your implementation of sortBy type checks. And it makes no use of dependent types. Separately to this function, you can write a proof that it's correct (if you want). Or unit tests, for that matter.

Re: Types

#108

Earlier quoted context omitted.

To some degree, you can choose what you want to prove about your algorithms in those languages. It's perfectly possible to implement a sorting algorithm without proving that it actually sorts the input, or indeed returns a permutation of the input list at all. In that sense, you can choose how much efford you want to put into it.

> To some degree, you can choose what you want to prove about your algorithms in those languages. > It's perfectly possible to implement a sorting algorithm without proving that it actually sorts the input, or indeed returns a permutation of the input list at all. Sure, but my point is that any nontrivial property requires the programmer to write difficult formal mathematical proofs which is an activity completely un…

> Sure, but my point is that any nontrivial property requires the programmer to write difficult formal mathematical proofs which is an activity completely unlike what you see in mainstream strongly typed languages (e.g. Haskell, OCaml).

So don't write the proofs then. Dependently typed languages don't require you to write proofs; they give you the ability to if you choose. Considering that it's impossible to write such proofs in OCaml or Haskell, it seems strange to complain that they're hard to write in a dependently typed language. "Hard" is strictly less difficult than "impossible".

Re: Types

#109

Decent overview of many concepts, but the opening line isn't strictly true: > A type is a collection of possible values. A type is a proposition, and "This binding has one of these possible values" is merely one type of proposition. So a type is much more powerful than a simple set-based interpretation, even though this is how most people think about it. For instance, in Haskell you can encode a region calculus that…

Well, there are several ways to think about types, especially regarding this article, which tries to cover static and dynamic types. Most formal definitions/treatments of types I've come across do not apply to dynamic types at all; usually the typing formalism says nothing about dynamically typed programs/values other than giving them one big recursive type. What we would informally call "dynamic types" are then trea…

> While such distinctions are certainly useful, an introductory/broad-overview explanation like this seems to benefit without such complications/distractions.

I don't think misrepresenting types is the way to go. An introduction like I just gave seems simple enough, where a type is a proposition about a program, and you just then explain that most such propositions are "variable X may take on values from set Y".

It's important not to perpetuate the false notion that sets of simple sets of values suffice to explain types, because it entails the common and equally false notion that full test coverage in a dynamically typed language suffices to match the safety of a statically typed language.

Re: Types

#110
post #104

Earlier quoted context omitted.

I'm not comparing dependent types vs. no dependent types. I'm comparing higher-order dependent types (Agda, Idris, Coq, etc.) vs. first-order dependent types: https://news.ycombinator.com/item?id=12350147 Seriously, Coq-style proof scripts are utterly unredable when they grow past a certain size. The only way to understand them is to replay them, so that you can see the intermediate hypotheses and goals.

I agree. Note that the Coq-style tactics-based approach is not the only possibility. In Agda, you are expected to write proofs in a functional style, similar to Haskell programs. There is a small amount of integrated automation, which helps you fill in holes in your proofs — however, the results are explicit proof terms, inserted at the right place in your program. Systems which behave in this fashion have the de Bru…

Thanks for the links (this one and the one from your other reply), I'm checking them.
Post reply on HN