Zulip Chat Archive
Stream: new members
Topic: functions that call partial functions not have to be partial
Asei Inoue (Jun 14 2024 at 14:04):
Why do functions that call partial functions not have to be partial?
partial def foo (x : Nat) : Nat := foo x
-- `bar` does not have to be marked as `partial`
def bar (x : Nat) := foo x
Asei Inoue (Jun 14 2024 at 14:07):
Functions that call unsafe
function have to be marked as unsafe
. What is the reason for this difference in behaviour between partial
and unsafe
?
/-
(kernel) invalid declaration, it uses unsafe declaration 'ptrAddrUnsafe'
-/
def fibonacci (n : Nat) : Array Nat := Id.run do
let mut fib : Array Nat := Array.mkEmpty n
fib := fib.push 0
fib := fib.push 1
for i in [2:n] do
dbg_trace ptrAddrUnsafe fib
fib := fib.push (fib[i-1]! + fib[i-2]!)
return fib
Intuitively, functions that call partial functions should be marked as partial because they cannot be guaranteed to terminate.
Mario Carneiro (Jun 14 2024 at 21:27):
The name is a bit of a misnomer, it is not a viral marker for partiality (as you have observed). Rather, it is a special variant of opaque
for compiling general recursive functions. When you see partial def foo
, this means that foo
cannot be unfolded to the thing on the other side of the :=
.
Asei Inoue (Jun 15 2024 at 13:56):
Thank you.
this means that
foo
cannot be unfolded to the thing on the other side of the:=
.
What this means? Definitions marked as partial
can be reduced as following: (opaque
marked definition can't be reduced)
partial def foo : Nat := 123
/-- info: 123 -/
#guard_msgs in #eval foo
/-- info: 123 -/
#guard_msgs in #reduce foo
opaque bar : String := "hello"
/-- info: bar -/
#guard_msgs in #reduce bar
Mario Carneiro (Jun 15 2024 at 18:37):
It seems lean ignores partial
on definitions that are not recursive. I guess that muddies the mental model a bit...
Asei Inoue (Jun 16 2024 at 06:21):
It might be better to make an error if you mark a non-recursive function as partial...
Damiano Testa (Jun 16 2024 at 06:51):
Maybe there is the need for an unused partial
linter. :smile:
Asei Inoue (Jun 16 2024 at 10:36):
Is it difficult to express information about whether a function stops or not at type level, which I hear is possible in the language such as koka?
Asei Inoue (Jun 16 2024 at 11:11):
I think this is related to algebraic effect... I think algebraic effect enables total/partial check in type theory...
Asei Inoue (Jun 16 2024 at 17:01):
Among other theorem proving support systems, F star seems to support algebraic effect; is it difficult to implement in Lean?
Henrik Böving (Jun 16 2024 at 17:41):
Having an efficient implementation of algebraic effects requires lots of investment in the compiler and runtime that we are most likely not going to undertake in the near future (potentially never).
Tracking non termination globally is not an issue, we can make partial
viral, it was merely decided not to do this. This does have a nice benefit which is that you can still prove things about functions that call partial
functions but are not themselves marked as partial.
Henrik Böving (Jun 16 2024 at 17:42):
Note that partial is also not a primitive of the system, it is a feature that comes from the combination of unsafe, opaque and implemented_by, so even if we were to make partial viral you do still not have a guarantee that your functions terminate, simply because people can imitate partial on their own.
Kyle Miller (Jun 16 2024 at 18:35):
Asei Inoue said:
Functions that call
unsafe
function have to be marked asunsafe
.
Note that it's possible to call unsafe
functions indirectly using opaque
/@[implemented_by]
to "launder" the fact that it's unsafe. You have to be very careful with doing this though because then the burden is on you to convince yourself that what you're doing is actually safe.
The unsafe ...
term is a way to do this whole thing automatically:
def fibonacci (n : Nat) : Array Nat := Id.run do
let mut fib : Array Nat := Array.mkEmpty n
fib := fib.push 0
fib := fib.push 1
for i in [2:n] do
dbg_trace unsafe ptrAddrUnsafe fib
fib := fib.push (fib[i-1]! + fib[i-2]!)
return fib
This is more-or-less safe here because dbg_trace unsafe ptrAddrUnsafe fib
doesn't functionally affect what fibonacci
does.
Kyle Miller (Jun 16 2024 at 18:37):
You can even prove things about this fibonacci
function. Here's an indirect proof of that claim:
def fibonacci (n : Nat) : Array Nat := Id.run do
let mut fib : Array Nat := Array.mkEmpty n
fib := fib.push 0
fib := fib.push 1
for i in [2:n] do
dbg_trace unsafe ptrAddrUnsafe fib
fib := fib.push (fib[i-1]! + fib[i-2]!)
return fib
def fibonacci' (n : Nat) : Array Nat := Id.run do
let mut fib : Array Nat := Array.mkEmpty n
fib := fib.push 0
fib := fib.push 1
for i in [2:n] do
fib := fib.push (fib[i-1]! + fib[i-2]!)
return fib
example : fibonacci = fibonacci' := rfl
Last updated: May 02 2025 at 03:31 UTC