Zulip Chat Archive

Stream: lean4

Topic: Total function not reducing


Locria Cyber (Jul 19 2022 at 16:16):

Hello, everyone. I am making an algebraic effect library.

Eff.lean:

namespace Eff

class inductive Has : {α: Type u} -> α -> List α -> Type u where
| zero : Has x (x::xs)
| succ : Has x xs -> Has x (y::xs)

-- any way to automate this?
instance : Has a (a :: xs) := Has.zero
instance [prev: Has a xs] : Has a (x::xs) := Has.succ prev


inductive Union : List (Type u -> Type v) -> Type u -> Type _ where
| mk : [_prf : Has ε εs] -> (x : ε α) -> Union εs α


example : Union [IO] Unit := Union.mk (IO.println "Hello")
axiom Example_Monad : Type -> Type
example : Union [Example_Monad, IO] Unit := Union.mk (IO.println "Hello")


inductive Free : (_monad: Type u -> Type v) -> (_res: Type u) -> Type _ where
  | pure : α -> Free ε α
  | bind : ε α -> (α -> Free ε β) -> Free ε β

instance : Pure (Free ε) where
  pure := Free.pure

def Free.recurbind (val: Free ε α) (cont: α -> Free ε β) : Free ε β :=
  match val with
  | Free.pure v => cont v
  | Free.bind mval mcont =>
    Free.bind mval (λ a' => Free.recurbind (mcont a') cont)

instance : Bind (Free ε) where
  bind := Free.recurbind


def Eff (es: List (Type u -> Type v)) : (_res: Type u) -> Type _ := Free (Union es)


example : Type 2 := Eff [IO] Nat


def lift [has: Has ε εs] (simple: ε α) : Eff εs α :=
  let un : Union εs α := Union.mk simple
  Free.bind un Free.pure

However, the function Eff above is not reducing.

Main.lean:

import Eff
open Eff

def main_eff : Eff [IO] Unit := do
  lift $ IO.println "hello"
  lift $ IO.println "hello"

error:

Main.lean:5:2

failed to synthesize instance
  Bind (Eff [IO])

Locria Cyber (Jul 19 2022 at 16:19):

How do I make Eff [IO] reduce to Free (Union [IO])?

Henrik Böving (Jul 19 2022 at 16:20):

By declaring your definition of Eff as an abbrev or tagging it with @[reducible] (equivalent):

abbrev Eff (es: List (Type u -> Type v)) : (_res: Type u) -> Type _ := Free (Union es)

or

@[reducible]
def Eff (es: List (Type u -> Type v)) : (_res: Type u) -> Type _ := Free (Union es)

Locria Cyber (Jul 19 2022 at 16:21):

Where can I find all the @[xxxx] "pragma"?

Locria Cyber (Jul 19 2022 at 16:21):

Also abbrev/lemma/def stuff

Locria Cyber (Jul 19 2022 at 16:21):

I can't find in tutorial

Henrik Böving (Jul 19 2022 at 16:23):

In an ideal world we would have a tutorial for all of this already but since we are at a very early stage the answer right now is: by reading other people's code, specifically parts of the Lean compiler and Stdlib as well as Mathlib are sources of good code.

And the alternative is to just ask here.

One could argue that mentioning abbrev in https://leanprover.github.io/theorem_proving_in_lean4/type_classes.html would definitely make sense though since its a very basic thing we do all the time.

Henrik Böving (Jul 19 2022 at 16:25):

Regarding types of declarations you have already found most though in main Lean we have

  • def, abbrev
  • axiom
  • structure, class
  • inductive, class inductive
  • axiom
  • opaque
  • instance
  • theorem
  • example

and that's all, lemma is a syntactic abbreviation for theorem that is added by mathlib

Sébastien Michelland (Jul 19 2022 at 16:25):

To clarify, @[reducible] marks the definition as reducible for typeclass inference specifically. By default typeclass inference avoids reducing because it would make the search very expensive.

Locria Cyber (Jul 19 2022 at 16:26):

what's the different between axiom and opaque

Locria Cyber (Jul 19 2022 at 16:28):

@Henrik Böving thanks! it works.

Henrik Böving (Jul 19 2022 at 16:28):

opaque requires you to provide a proof that the type is Inhabited which avoids inconsistencies. axiom can in theory allow you to introduce whatever you want.

Mac (Jul 19 2022 at 16:59):

Henrik Böving said:

opaque requires you to provide a proof that the type is Inhabited

Technically, it now just requires Nonempty, not Inhabited (with caveats):

opaque Foo : Type
@[instance] axiom Foo.nonempty : Nonempty Foo
unsafe def unsafeFoo : Foo := unsafeCast 0

opaque unimplFoo : Foo -- errors, noncomputable
noncomputable opaque noncomputableFoo : Foo -- works
@[implementedBy unsafeFoo] opaque implFoo : Foo -- works
@[extern c inline "0"] opaque externFoo : Foo -- works

Locria Cyber (Jul 19 2022 at 18:31):

Mac said:

Henrik Böving said:

opaque requires you to provide a proof that the type is Inhabited

Technically, it now just requires Nonempty, not Inhabited (with caveats):

opaque Foo : Type
@[instance] axiom Foo.nonempty : Nonempty Foo
unsafe def unsafeFoo : Foo := unsafeCast 0

opaque unimplFoo : Foo -- errors, noncomputable
noncomputable opaque noncomputableFoo : Foo -- works
@[implementedBy unsafeFoo] opaque implFoo : Foo -- works
@[extern c inline "0"] opaque externFoo : Foo -- works

I'm new to Lean. I have to remember all these keywords? :rolling_on_the_floor_laughing:

Henrik Böving (Jul 19 2022 at 18:40):

It gets easier over time :p and most of what he showed isn't relevant to everyday Lean don't worry

Locria Cyber (Jul 19 2022 at 18:40):

what the different between def and theorem?

Locria Cyber (Jul 19 2022 at 18:41):

Also, is there something like go or let ([a 1] [b 2]) body in lisp for inline recursion?

Henrik Böving (Jul 19 2022 at 18:43):

a theorem is always of type P where P : Prop, a def can be whatever.

inline recursion:

def iotaTR (n : Nat) : List Nat :=
  let rec go : Nat  List Nat  List Nat
    | 0, r => r.reverse
    | m@(n+1), r => go n (m::r)
  go n []

like this?

Locria Cyber (Jul 19 2022 at 18:45):

yes

Locria Cyber (Jul 19 2022 at 18:45):

How do I turn anything into a string

Locria Cyber (Jul 19 2022 at 18:46):

Sort and Type has any difference?

Henrik Böving (Jul 19 2022 at 18:48):

we have the docs4#ToString typeclass to turn stuff into strings, we have docs4#Repr for "debug representation" of data structure basically and we have format strings like: s!"Lalalala {variable} lalalalala"

There is an infinite hierarchy of types, we have

  1. Prop at the bottom where Prop is equivalent to Sort 0
  2. Type which is equivalent to Sort 1
  3. Type 1 which is equivalent to Sort 2
    and so on until infinity. The hierarchy exists in order to avoid self reference paradoxa

Locria Cyber (Jul 19 2022 at 18:49):

Prop is Sort 0?

Locria Cyber (Jul 19 2022 at 18:49):

I don't quite understand

Henrik Böving (Jul 19 2022 at 18:49):

Yes, they are aliases for each other

Henrik Böving (Jul 19 2022 at 18:51):

you can even ask Lean about this:

example : Sort 0 = Prop := by rfl
example : Sort 1 = Type := by rfl
example : Type u = Sort (u+1) := by rfl

Locria Cyber (Jul 19 2022 at 18:52):

What exactly is Prop?

Locria Cyber (Jul 19 2022 at 18:52):

There is also the difference between computable subset of Type 0 and "incomputable" Type 0

Locria Cyber (Jul 19 2022 at 18:53):

What exactly is Prop?

Kyle Miller (Jul 19 2022 at 18:53):

If x : p, y : p, and p : Prop, then x = y is true by rfl ("proof irrelevance") and there are also some special rules for universe levels when Prop is involved ("impredicativity" I believe?). It's meant to represent the universe of mathematical Propositions.

Henrik Böving (Jul 19 2022 at 18:53):

It is the type of mathematical Properties. So everything that we want to express as a logical statement in Lean has type Prop, equalities, inequalities, statements about correctness of certain functions etc.

Henrik Böving (Jul 19 2022 at 18:55):

In general if you have some statement p where p : Prop and you provide a value h : p what you have done is create a proof that p does indeed hold, this is the principle of how proofs work in Lean. What Kyle is describing above is a certain special property of Prop in Lean which says that if we have two proofs of the same statement we can always consider those proofs equivalent (it helps with code generation efficiency)

Kyle Miller (Jul 19 2022 at 18:57):

I suppose Prop's special properties were originally motivated by codegen, but it seems to be important for doing classical mathematics (like it's possible to define Subtype with the correct cardinality).

Kyle Miller (Jul 19 2022 at 18:58):

There's another thing about Prop that's special, which is its elimination rules -- in contrast to Type, they prevent you from getting data out of a proposition.

Kyle Miller (Jul 19 2022 at 18:58):

(It seems people have found this diagram to be useful, so here's a link.)

Kyle Miller (Jul 19 2022 at 19:01):

This page about foundations mentions everything I've mentioned about Prop.

Locria Cyber (Jul 19 2022 at 19:08):

What's the constructor for Prop?
Only _=_?

Reid Barton (Jul 19 2022 at 19:09):

No there are lots of ways to construct propositions, just like there are lots of ways to construct types.

Henrik Böving (Jul 19 2022 at 19:09):

No you are mis understanding, just like you can declare your own things in Type (Nat, String etc.) you can also declare your own things in Prop via inductive and structure, docs4#Eq, docs4#Nat.le etc.

Locria Cyber (Jul 19 2022 at 19:11):

So it's a black box?

Locria Cyber (Jul 19 2022 at 19:11):

I use Idris more and there is no such thing as Prop in Idris. The equality type Equal a is like inductive in Lean

Locria Cyber (Jul 19 2022 at 19:12):

Main> :doc Equal
Prelude.Equal : Prec
data Builtin.Equal : a -> b -> Type
  Totality: total
  Visibility: public export
  Constructor: Refl : x = x
  Hints:
    Equivalence ty Equal
    Euclidean ty Equal
    PartialEquivalence ty Equal
    Reflexive ty Equal
    Symmetric ty Equal
    Tolerance ty Equal
    Transitive ty Equal

Henrik Böving (Jul 19 2022 at 19:13):

Our equality type is also an inductive in Lean as I linked above: docs4#Eq it is just that we gave our mathematical propositions a special place to live in for the reasons outlined above

Locria Cyber (Jul 19 2022 at 19:15):

Kyle Miller said:

There's another thing about Prop that's special, which is its elimination rules -- in contrast to Type, they prevent you from getting data out of a proposition.

So Prop don't increase the universe level by one?

Locria Cyber (Jul 19 2022 at 19:19):

Locria Cyber said:

Eff.lean:

namespace Eff

class inductive Has : {α: Type u} -> α -> List α -> Type u where
| zero : Has x (x::xs)
| succ : Has x xs -> Has x (y::xs)

-- any way to automate this?
instance : Has a (a :: xs) := Has.zero
instance [prev: Has a xs] : Has a (x::xs) := Has.succ prev


inductive Union : List (Type u -> Type v) -> Type u -> Type _ where
| mk : [_prf : Has ε εs] -> (x : ε α) -> Union εs α


example : Union [IO] Unit := Union.mk (IO.println "Hello")
axiom Example_Monad : Type -> Type
example : Union [Example_Monad, IO] Unit := Union.mk (IO.println "Hello")


inductive Free : (_monad: Type u -> Type v) -> (_res: Type u) -> Type _ where
  | pure : α -> Free ε α
  | bind : ε α -> (α -> Free ε β) -> Free ε β

instance : Pure (Free ε) where
  pure := Free.pure

def Free.recurbind (val: Free ε α) (cont: α -> Free ε β) : Free ε β :=
  match val with
  | Free.pure v => cont v
  | Free.bind mval mcont =>
    Free.bind mval (λ a' => Free.recurbind (mcont a') cont)

instance : Bind (Free ε) where
  bind := Free.recurbind

In this case, I do need to take data out of Has e es though.

I need to take stacked monad apart to handle algebraic effects, and _prf in Union.mk is the position that I need to use.

In a sense, I need to pattern match Has e es, but it's never shown to the user.

Mac (Jul 20 2022 at 03:56):

Henrik Böving said:

a theorem is always of type P where P : Prop, a def can be whatever.

Technically, a theorem can be of any type:

theorem foo : Nat := 5 -- ok

I believe a theorem is essentially a noncomputable def (just with some syntactic sugar on top) :

theorem foo : Nat := 5
def addFoo (x : Nat) : Nat := x + foo
/-
error:
failed to compile definition, consider marking it as 'noncomputable'
because it depends on 'foo', and it does not have executable code
-/
noncomputable example (x : Nat) : Nat := x + foo -- ok

However, Henrik is right in spirit. We generally use a theorem for proposition we want to prove and a def for encoding concepts and generating code. I just enjoy demonstrating edge cases. :big_smile:

Mario Carneiro (Jul 20 2022 at 04:04):

Actually I think it's a noncomputable opaque


Last updated: Dec 20 2023 at 11:08 UTC