Zulip Chat Archive
Stream: general
Topic: IO error type
Greg Shuflin (Jan 15 2025 at 09:04):
I notice that https://leanprover-community.github.io/mathlib4_docs/Init/System/IO.html#IO.FS.createDir (and similar IO methods that are closely wrapping some POSIX filesystem function) return IO Unit
, and will throw an exception if something causes the IO operation to fail:
➤ bat test.lean
1 import Init.System
2
3 def main := do
4 IO.println "Creating /tmp/testdir"
5 IO.FS.createDir <| System.mkFilePath ["/tmp", "testdir"]
➤ lean --run test.lean
Creating /tmp/testdir
➤ lean --run test.lean
Creating /tmp/testdir
uncaught exception: already exists (error code: 17, file exists)
file: /tmp/testdir
There does exist an IOError type https://leanprover-community.github.io/mathlib4_docs/Init/System/IOError.html , but it seems like nothing is using it? Is there a reason why the IO.FS
functions don't use this type? Is there something I can do intelligently now to try to handle this uncaught exception in lean code without crashing the program?
Greg Shuflin (Jan 15 2025 at 09:08):
Also, what does this @&
syntax in https://github.com/leanprover/lean4/blob/128a1e6b0a82718382f9c39c79be44ff3284547c/src/Init/System/IO.lean#L444-L444 do?
Henrik Böving (Jan 15 2025 at 09:13):
Lean does not have exceptions in the classical Java sense, the IO
monad handles error handling and returns IO.Error
if an error happens so what you are asking for here is already happening. You can e.g. use the try
/catch
syntax to work with these errors.
The @&
syntax is relevant for reference counting when doing FFI, it tells the system to not increment the ref counter before calling that function, thus amounting to something like a "borrow" of the object.
Greg Shuflin (Jan 15 2025 at 09:14):
I tried asking Claude to explain it:
My question
I have an unrelated question. I'm trying to understand this line of code:
@[extern "lean_io_create_dir"] opaque createDir : @& FilePath → IO Unit
I know this is code that calls out to a foreign function written in C - but what does the @& syntax mean?
And claude's response:
The @& syntax in Lean indicates that a parameter is "borrowed" - this is a concept related to memory management and performance optimization. Let me break this down step by step:
In Lean's memory model, parameters can be passed in different ways that affect how they're handled under the hood. The @& annotation tells the compiler that we want to borrow the parameter rather than take ownership of it or make a copy.
Think of it like lending a book to someone. When you borrow a book:
You can read it and use it You can't modify it or keep it permanently The original owner retains ownership It's more efficient than making a copy of the whole book
In your specific example:
lean
@[extern "lean_io_create_dir"] opaque createDir : @& FilePath → IO Unit
The @& before FilePath means that createDir will borrow the filepath parameter temporarily while it creates the directory, but won't take ownership of it or modify it. This is particularly useful for foreign function interfaces (FFI) like this one because:
It reduces unnecessary copying of data between Lean and C It guarantees to the C code that the filepath parameter will remain valid during the function call It helps the Lean compiler optimize the code better
To make this more concrete, here's a comparison showing why borrowing can be important:
lean
-- Without borrowing, this might need to copy the filepath
def createDirectory (path : FilePath) : IO Unit := ...-- With borrowing, we can use the filepath directly without copying
def createDirectory (@& path : FilePath) : IO Unit := ...It's similar to Rust's borrowing system, if you're familiar with that, though Lean's version is somewhat simpler. Just as Rust uses & to indicate borrowing, Lean uses @& to indicate that a parameter is borrowed.
Would you like me to elaborate on any part of this, or would you like to explore how borrowing interacts with other features of Lean's type system?
I'm curious if there are any important gaps in what the AI told me
Greg Shuflin (Jan 15 2025 at 09:17):
Henrik Böving said:
Lean does not have exceptions in the classical Java sense, the
IO
monad handles error handling and returnsIO.Error
if an error happens so what you are asking for here is already happening. You can e.g. use thetry
/catch
syntax to work with these errors.
Ah okay, I'm used to a rust-style API where the equivalent IO functions return an explicit Result<(), IOError>
value and you use Rust's normal Result
-handling syntax.
Henrik Böving (Jan 15 2025 at 09:20):
That Result
is roughly equivalent to the IO
monad modulo additional tricks that you need to do for functional programming, I recommend reading up on monads if you are not familiar.
Greg Shuflin (Jan 15 2025 at 09:22):
I'm familiar with monads :smile: I'm just super-used to the rust way of describing monadic types and have to get used to the haskell-ish way again
Greg Shuflin (Jan 15 2025 at 09:24):
ah okay the IO
type is an alias for EIO IO.Error
, which is itself an alias for some more complicated monad stack
Last updated: May 02 2025 at 03:31 UTC