Zulip Chat Archive

Stream: new members

Topic: Classical vs constructive logic in computation


Rekkyn (Jan 26 2025 at 23:21):

I've heard that constructive logic plays better with computation compared to classical logic. But I've also heard that all Props are erased during computation, so it wouldn't seem to matter if a Prop was created in a classical or constructive way. Is it true that constructive logic is better for computations, and if so are there any examples of that?

Tristan Figueroa-Reid (Jan 27 2025 at 00:10):

Rekkyn said:

I've heard that constructive logic plays better with computation compared to classical logic. But I've also heard that all Props are erased during computation, so it wouldn't seem to matter if a Prop was created in a classical or constructive way. Is it true that constructive logic is better for computations, and if so are there any examples of that?

I don't have a good example for Prop at hand, but Props aren't the only things that can be created with classical logic (the axiom of choice is part of classical logic, and is the backing axiom for the LEM by Diaconescu's theorem)

An example of how classical logic wouldn't play well with computation is graph coloring: graph coloring can be made classically.

Tristan Figueroa-Reid (Jan 27 2025 at 00:15):

Correction: there is no good example for Props (assuming what you mean by "Props are erased by computation" is from #tpil (at Axioms and Computations))

Kyle Miller (Jan 27 2025 at 00:50):

I think that's a bit different from what Rekkyn is asking. That's a noncomputable instance, and it's noncomputable because it's using choice to "construct" a list of all the colorings. (Side note: we should change that to be a Finite instance!) There was never any hope that it would be computable.

For definitions, it doesn't matter one way or another if the proofs that appear in them are proved using choice or not. It all gets erased when the function is compiled for evaluation.

If you care about defeq properties, it's possible for something like Eq.rec or Acc.rec to get stuck on a classical proof (since in reduction proofs are not erased). I've never run into this as an issue however.

Sometimes people talk about writing proofs without constructive logic so that they can be reduced to compute witnesses "for free". That's probably where "better for computations" comes from. My understanding of the situation for Lean is (1) reducing proofs is largely a non-goal so things can get unnecessarily stuck because, for example, types aren't reduced and Eq.rec can get stuck during reduction of an otherwise "computable" definition and (2) people have found that reduction of proofs is not normalizing (#general > Normalization fails in lean @ 💬 ) so perhaps it's not good to try relying on this in Lean.

In practice, rather than writing proofs of existentials, you can make definitions that compute a witness and then write theorems that prove the witnesses have certain properties.

Kyle Miller (Jan 27 2025 at 00:56):

It's possible to work constructively in the sense of making sure to give algorithms for everything that is meaningfully computable, without subscribing to constructive logic, which is making the proofs themselves computable in some sense too. (I think CS outside of type theory tends not to ever think about avoiding LEM in proofs about algorithms for example. That doesn't make the algorithms any less computable.)

Aaron Liu (Jan 27 2025 at 01:31):

Kyle Miller said:

That's a noncomputable instance, and it's noncomputable because it's using choice to "construct" a list of all the colorings. (Side note: we should change that to be a Finite instance!) There was never any hope that it would be computable.

I think you can make it computable if you add DecidableRel G.Adj. Why give up on computability?

Rekkyn (Jan 27 2025 at 02:01):

Kyle Miller said:

I think that's a bit different from what Rekkyn is asking. That's a noncomputable instance, and it's noncomputable because it's using choice to "construct" a list of all the colorings. (Side note: we should change that to be a Finite instance!) There was never any hope that it would be computable.

For definitions, it doesn't matter one way or another if the proofs that appear in them are proved using choice or not. It all gets erased when the function is compiled for evaluation.

If you care about defeq properties, it's possible for something like Eq.rec or Acc.rec to get stuck on a classical proof (since in reduction proofs are not erased). I've never run into this as an issue however.

Sometimes people talk about writing proofs without constructive logic so that they can be reduced to compute witnesses "for free". That's probably where "better for computations" comes from. My understanding of the situation for Lean is (1) reducing proofs is largely a non-goal so things can get unnecessarily stuck because, for example, types aren't reduced and Eq.rec can get stuck during reduction of an otherwise "computable" definition and (2) people have found that reduction of proofs is not normalizing (#general > Normalization fails in lean @ 💬 ) so perhaps it's not good to try relying on this in Lean.

In practice, rather than writing proofs of existentials, you can make definitions that compute a witness and then write theorems that prove the witnesses have certain properties.

This makes sense, thanks! I'm guessing there's no reason why someone couldn't make "computable" versions of the built-in Props like this?

inductive TypeOr (a b : Prop) : Type where
  | inl (h : a) : TypeOr a b
  | inr (h : b) : TypeOr a b

Aaron Liu (Jan 27 2025 at 02:03):

My understanding is, there also hasn't been any reason for anyone to do this either.

Aaron Liu (Jan 27 2025 at 02:06):

If you want to emulate this behavior, use docs#PLift to change the arguments from Prop to Type, then use Sum/Prod/Sigma to construct the "computable" version.

Rekkyn (Jan 27 2025 at 02:12):

Aaron Liu said:

If you want to emulate this behavior, use docs#PLift to change the arguments from Prop to Type, then use Sum/Prod/Sigma to construct the "computable" version.

That's useful to know, thanks!

Kyle Miller (Jan 27 2025 at 03:31):

If you think you can make it computable (ideally with an efficient algorithm) then go ahead @Aaron Liu!

There are two points here:

  1. Noncomputable Fintype instances are useless. They should be Finite instances. This instance pre-dates Finite, otherwise I would have written it as a Finite instance.
  2. It is worth having Finite instances since they do not bring in unnecessary dependence on algorithms. Inside a proof, it does not matter at all that there's some arbitrary way to make a List of all simple graphs. Mere finiteness suffices.

Edward van de Meent (Jan 27 2025 at 08:13):

Inside a proof, it may matter if there is a computable list: if you're working with a concrete type and you want to do case analysis using fin_cases, you need a computable Fintype instance.

Aaron Liu (Jan 27 2025 at 14:09):

Kyle Miller said:

If you think you can make it computable (ideally with an efficient algorithm) then go ahead Aaron Liu!

For now, it's not efficient. Maybe I'll implement an efficient algorithm later.

Code

Aaron Liu (Jan 27 2025 at 22:14):

I had some time, so I made it more efficient (without tactics).

More Efficient

Kyle Miller (Jan 27 2025 at 22:32):

I'm not sure why it's written with all the proofs as proof terms — using tactics for proofs doesn't make definitions any less efficient, and using proof terms significantly harms readability.

Also, it's almost always better to use recursion instead of raw recursors. That motive doesn't look like it's complicated, so match ought to be able to handle the situation itself.

Aaron Liu (Jan 27 2025 at 22:35):

I tried match and it didn't work (not generalizing enough) (yes I tried (generalizing := true)), so I used Nat.rec directly.
I was writing it in term mode because "don't construct terms using tactics" and before I knew it, I had written the whole thing in terms.

I tried this on #eval Fintype.card ((⊤ : (SimpleGraph (Fin 8))).Coloring (Fin 7)) and this solution was 8 times faster than the other one I wrote.

Kyle Miller (Jan 27 2025 at 22:37):

That's fair, but if we want to include it in mathlib, we'll have to write it in a readable style. Ideally, we would factor out the key ideas into multiple definitions too. This is not an irreducibly complex computation.


Last updated: May 02 2025 at 03:31 UTC