Module docstring
{}
{}
IO.RealWorld
definition
: Type
/--
A representation of “the real world” that's used in `IO` monads to ensure that `IO` actions are not
reordered.
-/
/- Like <https://hackage.haskell.org/package/ghc-Prim-0.5.2.0/docs/GHC-Prim.html#t:RealWorld>.
Makes sure we never reorder `IO` operations.
TODO: mark opaque -/
def IO.RealWorld : Type := Unit
EIO
definition
(ε : Type) : Type → Type
/--
A monad that can have side effects on the external world or throw exceptions of type `ε`.
`BaseIO` is a version of this monad that cannot throw exceptions. `IO` sets the exception type to
`IO.Error`.
-/
/- TODO(Leo): mark it as an opaque definition. Reason: prevent
functions defined in other modules from accessing `IO.RealWorld`.
We don't want action such as
```
def getWorld : IO (IO.RealWorld) := get
```
-/
def EIO (ε : Type) : Type → Type := EStateM ε IO.RealWorld
instMonadEIO
instance
: Monad (EIO ε)
instance : Monad (EIO ε) := inferInstanceAs (Monad (EStateM ε IO.RealWorld))
instMonadFinallyEIO
instance
: MonadFinally (EIO ε)
instance : MonadFinally (EIO ε) := inferInstanceAs (MonadFinally (EStateM ε IO.RealWorld))
instMonadExceptOfEIO
instance
: MonadExceptOf ε (EIO ε)
instance : MonadExceptOf ε (EIO ε) := inferInstanceAs (MonadExceptOf ε (EStateM ε IO.RealWorld))
instOrElseEIO
instance
: OrElse (EIO ε α)
instance : OrElse (EIO ε α) := ⟨MonadExcept.orElse⟩
instInhabitedEIO
instance
[Inhabited ε] : Inhabited (EIO ε α)
instance [Inhabited ε] : Inhabited (EIO ε α) := inferInstanceAs (Inhabited (EStateM ε IO.RealWorld α))
BaseIO
definition
instMonadBaseIO
instance
: Monad BaseIO
instMonadFinallyBaseIO
instance
: MonadFinally BaseIO
instance : MonadFinally BaseIO := inferInstanceAs (MonadFinally (EIO Empty))
BaseIO.toEIO
definition
(act : BaseIO α) : EIO ε α
/--
Runs a `BaseIO` action, which cannot throw an exception, in any other `EIO` monad.
This function is usually used implicitly via [automatic monadic
lifting](lean-manual://section/lifting-monads) rather being than called explicitly.
-/
@[always_inline, inline]
def BaseIO.toEIO (act : BaseIO α) : EIO ε α :=
fun s => match act s with
| EStateM.Result.okEStateM.Result.ok a s => EStateM.Result.ok a s
instMonadLiftBaseIOEIO
instance
: MonadLift BaseIO (EIO ε)
instance : MonadLift BaseIO (EIO ε) := ⟨BaseIO.toEIO⟩
EIO.toBaseIO
definition
(act : EIO ε α) : BaseIO (Except ε α)
/--
Converts an `EIO ε` action that might throw an exception of type `ε` into an exception-free `BaseIO`
action that returns an `Except` value.
-/
@[always_inline, inline]
def EIO.toBaseIO (act : EIO ε α) : BaseIO (Except ε α) :=
fun s => match act s with
| EStateM.Result.okEStateM.Result.ok a s => EStateM.Result.ok (Except.ok a) s
| EStateM.Result.errorEStateM.Result.error ex s => EStateM.Result.ok (Except.error ex) s
EIO.catchExceptions
definition
(act : EIO ε α) (h : ε → BaseIO α) : BaseIO α
/--
Handles any exception that might be thrown by an `EIO ε` action, transforming it into an
exception-free `BaseIO` action.
-/
@[always_inline, inline]
def EIO.catchExceptions (act : EIO ε α) (h : ε → BaseIO α) : BaseIO α :=
fun s => match act s with
| EStateM.Result.okEStateM.Result.ok a s => EStateM.Result.ok a s
| EStateM.Result.errorEStateM.Result.error ex s => h ex s
EIO.ofExcept
definition
(e : Except ε α) : EIO ε α
/--
Converts an `Except ε` action into an `EIO ε` action.
If the `Except ε` action throws an exception, then the resulting `EIO ε` action throws the same
exception. Otherwise, the value is returned.
-/
def EIO.ofExcept (e : Except ε α) : EIO ε α :=
match e with
| Except.ok a => pure a
| Except.error e => throw e
IO
abbrev
: Type → Type
BaseIO.toIO
definition
(act : BaseIO α) : IO α
/--
Runs a `BaseIO` action, which cannot throw an exception, as an `IO` action.
This function is usually used implicitly via [automatic monadic
lifting](lean-manual://section/lifting-monads) rather than being called explicitly.
-/
@[inline] def BaseIO.toIO (act : BaseIO α) : IO α :=
act
EIO.toIO
definition
(f : ε → IO.Error) (act : EIO ε α) : IO α
/--
Converts an `EIO ε` action into an `IO` action by translating any exceptions that it throws into
`IO.Error`s using `f`.
-/
@[inline] def EIO.toIO (f : ε → IO.Error) (act : EIO ε α) : IO α :=
act.adaptExcept f
EIO.toIO'
definition
(act : EIO ε α) : IO (Except ε α)
IO.toEIO
definition
(f : IO.Error → ε) (act : IO α) : EIO ε α
/--
Runs an `IO` action in some other `EIO` monad, using `f` to translate `IO` exceptions.
-/
@[inline] def IO.toEIO (f : IO.Error → ε) (act : IO α) : EIO ε α :=
act.adaptExcept f
unsafeBaseIO
definition
(fn : BaseIO α) : α
/--
Executes arbitrary side effects in a pure context. This a **dangerous** operation that can easily
undermine important assumptions about the meaning of Lean programs, and it should only be used with
great care and a thorough understanding of compiler internals, and even then only to implement
observationally pure operations.
This function is not a good way to convert a `BaseIO α` into an `α`. Instead, use
[`do`-notation](lean-manual://section/do-notation).
Because the resulting value is treated as a side-effect-free term, the compiler may re-order,
duplicate, or delete calls to this function. The side effect may even be hoisted into a constant,
causing the side effect to occur at initialization time, even if it would otherwise never be called.
-/
@[inline] unsafe def unsafeBaseIO (fn : BaseIO α) : α :=
match fn.run () with
| EStateM.Result.okEStateM.Result.ok a _ => a
unsafeEIO
definition
(fn : EIO ε α) : Except ε α
/--
Executes arbitrary side effects in a pure context, with exceptions indicated via `Except`. This a
**dangerous** operation that can easily undermine important assumptions about the meaning of Lean
programs, and it should only be used with great care and a thorough understanding of compiler
internals, and even then only to implement observationally pure operations.
This function is not a good way to convert an `EIO α` or `IO α` into an `α`. Instead, use
[`do`-notation](lean-manual://section/do-notation).
Because the resulting value is treated as a side-effect-free term, the compiler may re-order,
duplicate, or delete calls to this function. The side effect may even be hoisted into a constant,
causing the side effect to occur at initialization time, even if it would otherwise never be called.
-/
@[inline] unsafe def unsafeEIO (fn : EIO ε α) : Except ε α :=
unsafeBaseIO fn.toBaseIO
unsafeIO
definition
(fn : IO α) : Except IO.Error α
timeit
opaque
(msg : @& String) (fn : IO α) : IO α
allocprof
opaque
(msg : @& String) (fn : IO α) : IO α
IO.initializing
opaque
: BaseIO Bool
/--
Returns `true` if and only if it is invoked during initialization.
Programs can execute `IO` actions during an initialization phase that occurs before the `main`
function is executed. The attribute `@[init <action>]` specifies which IO action is executed to set
the value of an opaque constant.
-/
@[extern "lean_io_initializing"] opaque IO.initializing : BaseIO Bool
IO.ofExcept
definition
[ToString ε] (e : Except ε α) : IO α
/--
Converts an `Except ε` action into an `IO` action.
If the `Except ε` action throws an exception, then the exception type's `ToString` instance is used
to convert it into an `IO.Error`, which is thrown. Otherwise, the value is returned.
-/
def ofExcept [ToString ε] (e : Except ε α) : IO α :=
match e with
| Except.ok a => pure a
| Except.error e => throw (IO.userError (toString e))
IO.lazyPure
definition
(fn : Unit → α) : IO α
IO.monoMsNow
opaque
: BaseIO Nat
IO.monoNanosNow
opaque
: BaseIO Nat
/--
Monotonically increasing time since an unspecified past point in nanoseconds. There is no relation
to wall clock time.
-/
@[extern "lean_io_mono_nanos_now"] opaque monoNanosNow : BaseIO Nat
IO.getRandomBytes
opaque
(nBytes : USize) : IO ByteArray
/--
Reads bytes from a system entropy source. It is not guaranteed to be cryptographically secure.
If `nBytes` is `0`, returns immediately with an empty buffer.
-/
@[extern "lean_io_get_random_bytes"] opaque getRandomBytes (nBytes : USize) : IO ByteArray
IO.sleep
definition
(ms : UInt32) : BaseIO Unit
IO.checkCanceled
opaque
: BaseIO Bool
/--
Checks whether the current task's cancellation flag has been set by calling `IO.cancel` or by
dropping the last reference to the task.
-/
@[extern "lean_io_check_canceled"] opaque checkCanceled : BaseIO Bool
IO.cancel
opaque
: @& Task α → BaseIO Unit
IO.TaskState
inductive
/-- The current state of a `Task` in the Lean runtime's task manager. -/
inductive TaskState
/--
The `Task` is waiting to be run.
It can be waiting for dependencies to complete or sitting in the task manager queue waiting for a
thread to run on.
-/
| waiting
/--
The `Task` is actively running on a thread or, in the case of a `Promise`, waiting for a call to
`IO.Promise.resolve`.
-/
| running
/--
The `Task` has finished running and its result is available. Calling `Task.get` or `IO.wait` on
the task will not block.
-/
| finished
deriving Inhabited, Repr, DecidableEq, Ord
IO.instInhabitedTaskState
instance
: Inhabited✝ (@IO.TaskState)
Inhabited, Repr, DecidableEq, Ord
IO.instReprTaskState
instance
: Repr✝ (@IO.TaskState✝)
Repr, DecidableEq, Ord
IO.instDecidableEqTaskState
instance
: DecidableEq✝ IO.TaskState
DecidableEq, Ord
IO.instOrdTaskState
instance
: Ord✝ (@IO.TaskState✝)
Ord
IO.instLTTaskState
instance
: LT TaskState
IO.instLETaskState
instance
: LE TaskState
IO.instMinTaskState
instance
: Min TaskState
IO.instMaxTaskState
instance
: Max TaskState
IO.TaskState.toString
definition
: TaskState → String
IO.instToStringTaskState
instance
: ToString TaskState
instance : ToString TaskState := ⟨TaskState.toString⟩
IO.getTaskState
opaque
: @& Task α → BaseIO TaskState
/--
Returns the current state of a task in the Lean runtime's task manager.
For tasks derived from `Promise`s, the states `waiting` and `running` should be considered
equivalent.
-/
@[extern "lean_io_get_task_state"] opaque getTaskState : @& Task α → BaseIO TaskState
IO.hasFinished
definition
(task : Task α) : BaseIO Bool
/--
Checks whether the task has finished execution, at which point calling `Task.get` will return
immediately.
-/
@[inline] def hasFinished (task : Task α) : BaseIO Bool := do
return (← getTaskState task) matches .finished
IO.wait
opaque
(t : Task α) : BaseIO α
/--
Waits for the task to finish, then returns its result.
-/
@[extern "lean_io_wait"] opaque wait (t : Task α) : BaseIO α :=
return t.get
IO.getNumHeartbeats
opaque
: BaseIO Nat
/--
Returns the number of _heartbeats_ that have occurred during the current thread's execution. The
heartbeat count is the number of “small” memory allocations performed in a thread.
Heartbeats used to implement timeouts that are more deterministic across different hardware.
-/
@[extern "lean_io_get_num_heartbeats"] opaque getNumHeartbeats : BaseIO Nat
IO.setNumHeartbeats
opaque
(count : Nat) : BaseIO Unit
/--
Sets the heartbeat counter of the current thread to the given amount. This can be used to avoid
counting heartbeats of code whose execution time is non-deterministic.
-/
@[extern "lean_io_set_heartbeats"] opaque setNumHeartbeats (count : Nat) : BaseIO Unit
IO.addHeartbeats
definition
(count : Nat) : BaseIO Unit
/--
Adjusts the heartbeat counter of the current thread by the given amount. This can be useful to give
allocation-avoiding code additional “weight” and is also used to adjust the counter after resuming
from a snapshot.
Heartbeats are a means of implementing “deterministic” timeouts. The heartbeat counter is the number
of “small” memory allocations performed on the current execution thread.
-/
def addHeartbeats (count : Nat) : BaseIO Unit := do
let n ← getNumHeartbeats
setNumHeartbeats (n + count)
IO.FS.Mode
inductive
/--
Whether a file should be opened for reading, writing, creation and writing, or appending.
A the operating system level, this translates to the mode of a file handle (i.e., a set of `open`
flags and an `fdopen` mode).
None of the modes represented by this datatype translate line endings (i.e. `O_BINARY` on Windows).
Furthermore, they are not inherited across process creation (i.e. `O_NOINHERIT` on Windows and
`O_CLOEXEC` elsewhere).
**Operating System Specifics:**
* Windows:
[`_open`](https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/open-wopen?view=msvc-170),
[`_fdopen`](https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fdopen-wfdopen?view=msvc-170)
* Linux: [`open`](https://linux.die.net/man/2/open), [`fdopen`](https://linux.die.net/man/3/fdopen)
-/
inductive FS.Mode where
/--
The file should be opened for reading.
The read/write cursor is positioned at the beginning of the file. It is an error if the file does
not exist.
* `open` flags: `O_RDONLY`
* `fdopen` mode: `r`
-/
| read
/--
The file should be opened for writing.
If the file already exists, it is truncated to zero length. Otherwise, a new file is created. The
read/write cursor is positioned at the beginning of the file.
* `open` flags: `O_WRONLY | O_CREAT | O_TRUNC`
* `fdopen` mode: `w`
-/
| write
/--
A new file should be created for writing.
It is an error if the file already exists. A new file is created, with the read/write cursor
positioned at the start.
* `open` flags: `O_WRONLY | O_CREAT | O_TRUNC | O_EXCL`
* `fdopen` mode: `w`
-/
| writeNew
/--
The file should be opened for both reading and writing.
It is an error if the file does not already exist. The read/write cursor is positioned at the
start of the file.
* `open` flags: `O_RDWR`
* `fdopen` mode: `r+`
-/
| readWrite
/--
The file should be opened for writing.
If the file does not already exist, it is created. If the file already exists, it is opened, and
the read/write cursor is positioned at the end of the file.
* `open` flags: `O_WRONLY | O_CREAT | O_APPEND`
* `fdopen` mode: `a`
-/
| append
IO.FS.Handle
opaque
: Type
/--
A reference to an opened file.
File handles wrap the underlying operating system's file descriptors. There is no explicit operation
to close a file: when the last reference to a file handle is dropped, the file is closed
automatically.
Handles have an associated read/write cursor that determines the where reads and writes occur in the
file.
-/
opaque FS.Handle : Type := Unit
IO.FS.Stream
structure
/--
A pure-Lean abstraction of POSIX streams. These streams may represent an underlying POSIX stream or
be implemented by Lean code.
Because standard input, standard output, and standard error are all `IO.FS.Stream`s that can be
overridden, Lean code may capture and redirect input and output.
-/
structure FS.Stream where
/--
Flushes the stream's output buffers.
-/
flush : IO Unit
/--
Reads up to the given number of bytes from the stream.
If the returned array is empty, an end-of-file marker (EOF) has been reached. An EOF does not
actually close a stream, so further reads may block and return more data.
-/
read : USize → IO ByteArray
/--
Writes the provided bytes to the stream.
If the stream represents a physical output device such as a file on disk, then the results may be
buffered. Call `FS.Stream.flush` to synchronize their contents.
-/
write : ByteArray → IO Unit
/--
Reads text up to and including the next newline from the stream.
If the returned string is empty, an end-of-file marker (EOF) has been reached.
An EOF does not actually close a stream, so further reads may block and return more data.
-/
getLine : IO String
/--
Writes the provided string to the stream.
-/
putStr : String → IO Unit
/-- Returns `true` if a stream refers to a Windows console or Unix terminal. -/
isTty : BaseIO Bool
deriving Inhabited
IO.FS.instInhabitedStream
instance
: Inhabited✝ (@IO.FS.Stream)
Inhabited
IO.getStdin
opaque
: BaseIO FS.Stream
IO.getStdout
opaque
: BaseIO FS.Stream
IO.getStderr
opaque
: BaseIO FS.Stream
IO.setStdin
opaque
: FS.Stream → BaseIO FS.Stream
IO.setStdout
opaque
: FS.Stream → BaseIO FS.Stream
IO.setStderr
opaque
: FS.Stream → BaseIO FS.Stream
IO.iterate
definition
(a : α) (f : α → IO (Sum α β)) : IO β
/--
Iterates an `IO` action. Starting with an initial state, the action is applied repeatedly until it
returns a final value in `Sum.inr`. Each time it returns `Sum.inl`, the returned value is treated as
a new sate.
-/
@[specialize] partial def iterate (a : α) (f : α → IO (Sum α β)) : IO β := do
let v ← f a
match v with
| Sum.inl a => iterate a f
| Sum.inr b => pure b
IO.FS.Handle.mk
opaque
(fn : @& FilePath) (mode : FS.Mode) : IO Handle
IO.FS.Handle.unlock
opaque
(h : @& Handle) : IO Unit
IO.FS.Handle.isTty
opaque
(h : @& Handle) : BaseIO Bool
IO.FS.Handle.flush
opaque
(h : @& Handle) : IO Unit
IO.FS.Handle.rewind
opaque
(h : @& Handle) : IO Unit
IO.FS.Handle.truncate
opaque
(h : @& Handle) : IO Unit
/--
Truncates the handle to its read/write cursor.
This operation does not automatically flush output buffers, so the contents of the output device may
not reflect the change immediately. This does not usually lead to problems because the read/write
cursor includes buffered writes. However, buffered writes followed by `IO.FS.Handle.rewind`, then
`IO.FS.Handle.truncate`, and then closing the file may lead to a non-empty file. If unsure, call
`IO.FS.Handle.flush` before truncating.
-/
@[extern "lean_io_prim_handle_truncate"] opaque truncate (h : @& Handle) : IO Unit
IO.FS.Handle.read
opaque
(h : @& Handle) (bytes : USize) : IO ByteArray
/--
Reads up to the given number of bytes from the handle. If the returned array is empty, an
end-of-file marker (EOF) has been reached.
Encountering an EOF does not close a handle. Subsequent reads may block and return more data.
-/
@[extern "lean_io_prim_handle_read"] opaque read (h : @& Handle) (bytes : USize) : IO ByteArray
IO.FS.Handle.write
opaque
(h : @& Handle) (buffer : @& ByteArray) : IO Unit
/--
Writes the provided bytes to the the handle.
Writing to a handle is typically buffered, and may not immediately modify the file on disk. Use
`IO.FS.Handle.flush` to write changes to buffers to the associated device.
-/
@[extern "lean_io_prim_handle_write"] opaque write (h : @& Handle) (buffer : @& ByteArray) : IO Unit
IO.FS.Handle.getLine
opaque
(h : @& Handle) : IO String
/--
Reads UTF-8-encoded text up to and including the next line break from the handle. If the returned
string is empty, an end-of-file marker (EOF) has been reached.
Encountering an EOF does not close a handle. Subsequent reads may block and return more data.
-/
@[extern "lean_io_prim_handle_get_line"] opaque getLine (h : @& Handle) : IO String
IO.FS.Handle.putStr
opaque
(h : @& Handle) (s : @& String) : IO Unit
/--
Writes the provided string to the file handle using the UTF-8 encoding.
Writing to a handle is typically buffered, and may not immediately modify the file on disk. Use
`IO.FS.Handle.flush` to write changes to buffers to the associated device.
-/
@[extern "lean_io_prim_handle_put_str"] opaque putStr (h : @& Handle) (s : @& String) : IO Unit
IO.FS.realPath
opaque
(fname : FilePath) : IO FilePath
/--
Resolves a path to an absolute path that contains no '.', '..', or symbolic links.
This function coincides with the [POSIX `realpath`
function](https://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html).
-/
@[extern "lean_io_realpath"] opaque realPath (fname : FilePath) : IO FilePath
IO.FS.removeFile
opaque
(fname : @& FilePath) : IO Unit
/--
Removes (deletes) a file from the filesystem.
To remove a directory, use `IO.FS.removeDir` or `IO.FS.removeDirAll` instead.
-/
@[extern "lean_io_remove_file"] opaque removeFile (fname : @& FilePath) : IO Unit
IO.FS.removeDir
opaque
: @& FilePath → IO Unit
IO.FS.createDir
opaque
: @& FilePath → IO Unit
IO.FS.rename
opaque
(old new : @& FilePath) : IO Unit
IO.FS.createTempFile
opaque
: IO (Handle × FilePath)
/--
Creates a temporary file in the most secure manner possible, returning both a `Handle` to the
already-opened file and its path.
There are no race conditions in the file’s creation. The file is readable and writable only by the
creating user ID. Additionally on UNIX style platforms the file is executable by nobody.
It is the caller's job to remove the file after use. Use `withTempFile` to ensure that the temporary
file is removed.
-/
@[extern "lean_io_create_tempfile"] opaque createTempFile : IO (HandleHandle × FilePath)
IO.FS.createTempDir
opaque
: IO FilePath
/--
Creates a temporary directory in the most secure manner possible, returning the new directory's
path. There are no race conditions in the directory’s creation. The directory is readable and
writable only by the creating user ID.
It is the caller's job to remove the directory after use. Use `withTempDir` to ensure that the
temporary directory is removed.
-/
@[extern "lean_io_create_tempdir"] opaque createTempDir : IO FilePath
IO.getEnv
opaque
(var : @& String) : BaseIO (Option String)
IO.appPath
opaque
: IO FilePath
IO.currentDir
opaque
: IO FilePath
/--
Returns the current working directory of the executing process.
-/
@[extern "lean_io_current_dir"] opaque currentDir : IO FilePath
IO.FS.withFile
definition
(fn : FilePath) (mode : Mode) (f : Handle → IO α) : IO α
/--
Opens the file `fn` with the specified `mode` and passes the resulting file handle to `f`.
The file handle is closed when the last reference to it is dropped. If references escape `f`, then
the file remains open even after `IO.FS.withFile` has finished.
-/
@[inline]
def withFile (fn : FilePath) (mode : Mode) (f : Handle → IO α) : IO α :=
Handle.mkHandle.mk fn mode >>= f
IO.FS.Handle.putStrLn
definition
(h : Handle) (s : String) : IO Unit
IO.FS.Handle.readBinToEndInto
definition
(h : Handle) (buf : ByteArray) : IO ByteArray
/--
Reads the entire remaining contents of the file handle until an end-of-file marker (EOF) is
encountered.
The underlying file is not automatically closed upon encountering an EOF, and subsequent reads from
the handle may block and/or return data.
-/
partial def Handle.readBinToEndInto (h : Handle) (buf : ByteArray) : IO ByteArray := do
let rec loop (acc : ByteArray) : IO ByteArray := do
let buf ← h.read 1024
if buf.isEmpty then
return acc
else
loop (acc ++ buf)
loop buf
IO.FS.Handle.readBinToEnd
definition
(h : Handle) : IO ByteArray
/--
Reads the entire remaining contents of the file handle until an end-of-file marker (EOF) is
encountered.
The underlying file is not automatically closed upon encountering an EOF, and subsequent reads from
the handle may block and/or return data.
-/
partial def Handle.readBinToEnd (h : Handle) : IO ByteArray := do
h.readBinToEndInto .empty
IO.FS.Handle.readToEnd
definition
(h : Handle) : IO String
/--
Reads the entire remaining contents of the file handle as a UTF-8-encoded string. An exception is
thrown if the contents are not valid UTF-8.
The underlying file is not automatically closed, and subsequent reads from the handle may block
and/or return data.
-/
def Handle.readToEnd (h : Handle) : IO String := do
let data ← h.readBinToEnd
match String.fromUTF8? data with
| somesome s => return s
| none => throw <| .userError s!"Tried to read from handle containing non UTF-8 data."
IO.FS.lines
definition
(fname : FilePath) : IO (Array String)
/--
Returns the contents of a UTF-8-encoded text file as an array of lines.
Newline markers are not included in the lines.
-/
partial def lines (fname : FilePath) : IO (Array String) := do
let h ← Handle.mk fname Mode.read
let rec read (lines : Array String) := do
let line ← h.getLine
if line.length == 0 then
pure lines
else if line.back == '\n' then
let line := line.dropRight 1
let line := if line.back == '\r' then line.dropRight 1 else line
read <| lines.push line
else
pure <| lines.push line
read #[]
IO.FS.writeBinFile
definition
(fname : FilePath) (content : ByteArray) : IO Unit
/--
Write the provided bytes to a binary file at the specified path.
-/
def writeBinFile (fname : FilePath) (content : ByteArray) : IO Unit := do
let h ← Handle.mk fname Mode.write
h.write content
IO.FS.writeFile
definition
(fname : FilePath) (content : String) : IO Unit
IO.FS.Stream.putStrLn
definition
(strm : FS.Stream) (s : String) : IO Unit
IO.FS.DirEntry
structure
IO.FS.instReprDirEntry
instance
: Repr✝ (@IO.FS.DirEntry✝)
Repr
IO.FS.DirEntry.path
definition
(entry : DirEntry) : FilePath
/-- The path of the file indicated by the directory entry. -/
def DirEntry.path (entry : DirEntry) : FilePath :=
entry.root / entry.fileName
IO.FS.FileType
inductive
/-- Types of files that may be found on a filesystem. -/
inductive FileType where
/-- Directories don't have contents, but may contain other files. -/
| dir
/-- Ordinary files that have contents and are not directories. -/
| file
/-- Symbolic links that are pointers to other named files. -/
| symlink
/-- Files that are neither ordinary files, directories, or symbolic links. -/
| other
deriving Repr, BEq
IO.FS.instReprFileType
instance
: Repr✝ (@IO.FS.FileType✝)
IO.FS.instBEqFileType
instance
: BEq✝ (@IO.FS.FileType✝)
BEq
IO.FS.SystemTime
structure
IO.FS.instReprSystemTime
instance
: Repr✝ (@IO.FS.SystemTime✝)
IO.FS.instBEqSystemTime
instance
: BEq✝ (@IO.FS.SystemTime✝)
IO.FS.instOrdSystemTime
instance
: Ord✝ (@IO.FS.SystemTime✝)
IO.FS.instInhabitedSystemTime
instance
: Inhabited✝ (@IO.FS.SystemTime)
Inhabited
IO.FS.instLTSystemTime
instance
: LT SystemTime
instance : LT SystemTime := ltOfOrd
IO.FS.instLESystemTime
instance
: LE SystemTime
instance : LE SystemTime := leOfOrd
IO.FS.Metadata
structure
/--
File metadata.
The metadata for a file can be accessed with `System.FilePath.metadata`.
-/
structure Metadata where
--permissions : ...
/-- File access time. -/
accessed : SystemTime
/-- File modification time. -/
modified : SystemTime
/-- The size of the file in bytes. -/
byteSize : UInt64
/--
Whether the file is an ordinary file, a directory, a symbolic link, or some other kind of file.
-/
type : FileType
deriving Repr
IO.FS.instReprMetadata
instance
: Repr✝ (@IO.FS.Metadata✝)
Repr
System.FilePath.readDir
opaque
: @& FilePath → IO (Array IO.FS.DirEntry)
/--
Returns the contents of the indicated directory. Throws an exception if the file does not exist or
is not a directory.
-/
@[extern "lean_io_read_dir"]
opaque readDir : @& FilePath → IO (Array IO.FS.DirEntry)
System.FilePath.metadata
opaque
: @& FilePath → IO IO.FS.Metadata
/--
Returns metadata for the indicated file. Throws an exception if the file does not exist or the
metadata cannot be accessed.
-/
@[extern "lean_io_metadata"]
opaque metadata : @& FilePath → IO IO.FS.Metadata
System.FilePath.isDir
definition
(p : FilePath) : BaseIO Bool
/--
Checks whether the indicated path can be read and is a directory.
-/
def isDir (p : FilePath) : BaseIO Bool := do
match (← p.metadata.toBaseIO) with
| Except.ok m => return m.type == IO.FS.FileType.dir
| Except.error _ => return false
System.FilePath.pathExists
definition
(p : FilePath) : BaseIO Bool
/--
Checks whether the indicated path points to a file that exists.
-/
def pathExists (p : FilePath) : BaseIO Bool :=
return (← p.metadata.toBaseIO).toBool
IO.FS.readBinFile
definition
(fname : FilePath) : IO ByteArray
/--
Reads the entire contents of the binary file at the given path as an array of bytes.
-/
def readBinFile (fname : FilePath) : IO ByteArray := do
-- Requires metadata so defined after metadata
let mdata ← fname.metadata
let size := mdata.byteSize.toUSize
let handle ← IO.FS.Handle.mk fname .read
let buf ←
if size > 0 then
handle.read mdata.byteSize.toUSize
else
pure <| ByteArray.emptyWithCapacity 0
handle.readBinToEndInto buf
IO.FS.readFile
definition
(fname : FilePath) : IO String
/--
Reads the entire contents of the UTF-8-encoded file at the given path as a `String`.
An exception is thrown if the contents of the file are not valid UTF-8. This is in addition to
exceptions that may always be thrown as a result of failing to read files.
-/
def readFile (fname : FilePath) : IO String := do
let data ← readBinFile fname
match String.fromUTF8? data with
| somesome s => return s
| none => throw <| .userError s!"Tried to read file '{fname}' containing non UTF-8 data."
IO.withStdin
definition
[Monad m] [MonadFinally m] [MonadLiftT BaseIO m] (h : FS.Stream) (x : m α) : m α
/--
Runs an action with the specified stream `h` as standard input, restoring the original standard
input stream afterwards.
-/
def withStdin [Monad m] [MonadFinally m] [MonadLiftT BaseIO m] (h : FS.Stream) (x : m α) : m α := do
let prev ← setStdin h
try x finally discard <| setStdin prev
IO.withStdout
definition
[Monad m] [MonadFinally m] [MonadLiftT BaseIO m] (h : FS.Stream) (x : m α) : m α
/--
Runs an action with the specified stream `h` as standard output, restoring the original standard
output stream afterwards.
-/
def withStdout [Monad m] [MonadFinally m] [MonadLiftT BaseIO m] (h : FS.Stream) (x : m α) : m α := do
let prev ← setStdout h
try
x
finally
discard <| setStdout prev
IO.withStderr
definition
[Monad m] [MonadFinally m] [MonadLiftT BaseIO m] (h : FS.Stream) (x : m α) : m α
/--
Runs an action with the specified stream `h` as standard error, restoring the original standard
error stream afterwards.
-/
def withStderr [Monad m] [MonadFinally m] [MonadLiftT BaseIO m] (h : FS.Stream) (x : m α) : m α := do
let prev ← setStderr h
try x finally discard <| setStderr prev
IO.print
definition
[ToString α] (s : α) : IO Unit
IO.println
definition
[ToString α] (s : α) : IO Unit
IO.eprint
definition
[ToString α] (s : α) : IO Unit
IO.eprintln
definition
[ToString α] (s : α) : IO Unit
IO.appDir
definition
: IO FilePath
/--
Returns the directory that the current executable is located in.
-/
def appDir : IO FilePath := do
let p ← appPath
let some p ← pure p.parent
| throw <| IO.userError s!"IO.appDir: unexpected filename '{p}'"
FS.realPath p
IO.FS.createDirAll
definition
(p : FilePath) : IO Unit
/--
Creates a directory at the specified path, creating all missing parents as directories.
-/
partial def createDirAll (p : FilePath) : IO Unit := do
if ← p.isDir then
return ()
if let some parent := p.parent then
createDirAll parent
try
createDir p
catch
| e =>
if ← p.isDir then
pure () -- I guess someone else was faster
else
throw e
IO.FS.removeDirAll
definition
(p : FilePath) : IO Unit
/--
Fully remove given directory by deleting all contained files and directories in an unspecified order.
Fails if any contained entry cannot be deleted or was newly created during execution. -/
partial def removeDirAll (p : FilePath) : IO Unit := do
for ent in (← p.readDir) do
if (← ent.path.isDir : Bool) then
removeDirAll ent.path
else
removeFile ent.path
removeDir p
IO.FS.withTempFile
definition
[Monad m] [MonadFinally m] [MonadLiftT IO m] (f : Handle → FilePath → m α) : m α
/--
Creates a temporary file in the most secure manner possible and calls `f` with both a `Handle` to
the already-opened file and its path. Afterwards, the temporary file is deleted.
There are no race conditions in the file’s creation. The file is readable and writable only by the
creating user ID. Additionally on UNIX style platforms the file is executable by nobody.
Use `IO.FS.createTempFile` to avoid the automatic deletion of the temporary file.
-/
def withTempFile [Monad m] [MonadFinally m] [MonadLiftT IO m] (f : Handle → FilePath → m α) :
m α := do
let (handle, path) ← createTempFile
try
f handle path
finally
removeFile path
IO.FS.withTempDir
definition
[Monad m] [MonadFinally m] [MonadLiftT IO m] (f : FilePath → m α) : m α
/--
Creates a temporary directory in the most secure manner possible, providing a its path to an `IO`
action. Afterwards, all files in the temporary directory are recursively deleted, regardless of how
or when they were created.
There are no race conditions in the directory’s creation. The directory is readable and writable
only by the creating user ID. Use `IO.FS.createTempDir` to avoid the automatic deletion of the
directory's contents.
-/
def withTempDir [Monad m] [MonadFinally m] [MonadLiftT IO m] (f : FilePath → m α) :
m α := do
let path ← createTempDir
try
f path
finally
removeDirAll path
IO.Process.getCurrentDir
opaque
: IO FilePath
/-- Returns the current working directory of the calling process. -/
@[extern "lean_io_process_get_current_dir"] opaque getCurrentDir : IO FilePath
IO.Process.setCurrentDir
opaque
(path : @& FilePath) : IO Unit
/-- Sets the current working directory of the calling process. -/
@[extern "lean_io_process_set_current_dir"] opaque setCurrentDir (path : @& FilePath) : IO Unit
IO.Process.getPID
opaque
: BaseIO UInt32
IO.Process.Stdio
inductive
/--
Whether the standard input, output, and error handles of a child process should be attached to
pipes, inherited from the parent, or null.
If the stream is a pipe, then the parent process can use it to communicate with the child.
-/
inductive Stdio where
/-- The stream should be attached to a pipe. -/
| piped
/-- The stream should be inherited from the parent process. -/
| inherit
/-- The stream should be empty. -/
| null
IO.Process.Stdio.toHandleType
definition
: Stdio → Type
/--
The type of handles that can be used to communicate with a child process on its standard input,
output, or error streams.
For `IO.Process.Stdio.piped`, this type is `IO.FS.Handle`. Otherwise, it is `Unit`, because no
communication is possible.
-/
def Stdio.toHandleType : Stdio → Type
| Stdio.piped => FS.Handle
| Stdio.inherit => Unit
| Stdio.null => Unit
IO.Process.StdioConfig
structure
/--
Configuration for the standard input, output, and error handles of a child process.
-/
structure StdioConfig where
/-- Configuration for the process' stdin handle. -/
stdin := Stdio.inherit
/-- Configuration for the process' stdout handle. -/
stdout := Stdio.inherit
/-- Configuration for the process' stderr handle. -/
stderr := Stdio.inherit
IO.Process.SpawnArgs
structure
extends StdioConfig
/--
Configuration for a child process to be spawned.
Use `IO.Process.spawn` to start the child process. `IO.Process.output` and `IO.Process.run` can be
used when the child process should be run to completion, with its output and/or error code captured.
-/
structure SpawnArgs extends StdioConfig where
/-- Command name. -/
cmd : String
/-- Arguments for the command. -/
args : Array String := #[]
/-- The child process's working directory. Inherited from the parent current process if `none`. -/
cwd : Option FilePath := none
/--
Add or remove environment variables for the child process.
The child process inherits the parent's environment, as modified by `env`. Keys in the array are
the names of environment variables. A `none`, causes the entry to be removed from the environment,
and `some` sets the variable to the new value, adding it if necessary. Variables are processed from left to right.
-/
env : Array (StringString × Option String) := #[]
/--
Starts the child process in a new session and process group using `setsid`. Currently a no-op on
non-POSIX platforms.
-/
setsid : Bool := false
IO.Process.Child
structure
(cfg : StdioConfig)
/--
A child process that was spawned with configuration `cfg`.
The configuration determines whether the child process's standard input, standard output, and
standard error are `IO.FS.Handle`s or `Unit`.
-/
-- TODO(Sebastian): constructor must be private
structure Child (cfg : StdioConfig) where
/--
The child process's standard input handle, if it was configured as `IO.Process.Stdio.piped`, or
`()` otherwise.
-/
stdin : cfg.stdin.toHandleType
/--
The child process's standard output handle, if it was configured as `IO.Process.Stdio.piped`, or
`()` otherwise.
-/
stdout : cfg.stdout.toHandleType
/--
The child process's standard error handle, if it was configured as `IO.Process.Stdio.piped`, or
`()` otherwise.
-/
stderr : cfg.stderr.toHandleType
IO.Process.spawn
opaque
(args : SpawnArgs) : IO (Child args.toStdioConfig)
/--
Starts a child process with the provided configuration. The child process is spawned using operating
system primitives, and it can be written in any language.
The child process runs in parallel with the parent.
If the child process's standard input is a pipe, use `IO.Process.Child.takeStdin` to make it
possible to close the child's standard input before the process terminates, which provides the child with an end-of-file marker.
-/
@[extern "lean_io_process_spawn"] opaque spawn (args : SpawnArgs) : IO (Child args.toStdioConfig)
IO.Process.Child.wait
opaque
{cfg : @& StdioConfig} : @& Child cfg → IO UInt32
/--
Blocks until the child process has exited and return its exit code.
-/
@[extern "lean_io_process_child_wait"] opaque Child.wait {cfg : @& StdioConfig} : @& Child cfg → IO UInt32
IO.Process.Child.tryWait
opaque
{cfg : @& StdioConfig} : @& Child cfg → IO (Option UInt32)
/--
Checks whether the child has exited. Returns `none` if the process has not exited, or its exit code
if it has.
-/
@[extern "lean_io_process_child_try_wait"] opaque Child.tryWait {cfg : @& StdioConfig} : @& Child cfg →
IO (Option UInt32)
IO.Process.Child.kill
opaque
{cfg : @& StdioConfig} : @& Child cfg → IO Unit
/--
Terminates the child process using the `SIGTERM` signal or a platform analogue.
If the process was started using `SpawnArgs.setsid`, terminates the entire process group instead.
-/
@[extern "lean_io_process_child_kill"] opaque Child.kill {cfg : @& StdioConfig} : @& Child cfg → IO Unit
IO.Process.Child.takeStdin
opaque
{cfg : @& StdioConfig} : Child cfg → IO (cfg.stdin.toHandleType × Child { cfg with stdin := Stdio.null })
/--
Extracts the `stdin` field from a `Child` object, allowing the handle to be closed while maintaining
a reference to the child process.
File handles are closed when the last reference to them is dropped. Closing the child's standard
input causes an end-of-file marker. Because the `Child` object has a reference to the standard
input, this operation is necessary in order to close the stream while the process is running (e.g.
to extract its exit code after calling `Child.wait`). Many processes do not terminate until their
standard input is exhausted.
-/
@[extern "lean_io_process_child_take_stdin"] opaque Child.takeStdin {cfg : @& StdioConfig} : Child cfg →
IO (cfg.stdin.toHandleType × Child { cfg with stdin := Stdio.null })
IO.Process.Output
structure
/--
The result of running a process to completion.
-/
structure Output where
/-- The process's exit code. -/
exitCode : UInt32
/-- Everything that was written to the process's standard output. -/
stdout : String
/-- Everything that was written to the process's standard error. -/
stderr : String
IO.Process.output
definition
(args : SpawnArgs) : IO Output
/--
Runs a process to completion and captures its output and exit code. The child process is run with a
null standard input, and the current process blocks until it has run to completion.
The specifications of standard input, output, and error handles in `args` are ignored.
-/
def output (args : SpawnArgs) : IO Output := do
let child ← spawn { args with stdout := .piped, stderr := .piped, stdin := .null }
let stdout ← IO.asTask child.stdout.readToEnd Task.Priority.dedicated
let stderr ← child.stderr.readToEnd
let exitCode ← child.wait
let stdout ← IO.ofExcept stdout.get
pure { exitCode := exitCode, stdout := stdout, stderr := stderr }
IO.Process.run
definition
(args : SpawnArgs) : IO String
/--
Runs a process to completion, blocking until it terminates. If the child process terminates
successfully with exit code 0, its standard output is returned. An exception is thrown if it
terminates with any other exit code.
-/
def run (args : SpawnArgs) : IO String := do
let out ← output args
if out.exitCode != 0 then
throw <| IO.userError <| "process '" ++ args.cmd ++ "' exited with code " ++ toString out.exitCode
pure out.stdout
IO.Process.exit
opaque
: UInt8 → IO α
IO.getTID
opaque
: BaseIO UInt64
IO.AccessRight
structure
/--
POSIX-style file permissions.
The `FileRight` structure describes these permissions for a file's owner, members of it's designated
group, and all others.
-/
structure AccessRight where
/-- The file can be read. -/
read : Bool := false
/-- The file can be written to. -/
write : Bool := false
/-- The file can be executed. -/
execution : Bool := false
IO.AccessRight.flags
definition
(acc : AccessRight) : UInt32
/--
Converts individual POSIX-style file permissions to their conventional three-bit representation.
This is the bitwise `or` of the following:
* If the file can be read, `0x4`, otherwise `0`.
* If the file can be written, `0x2`, otherwise `0`.
* If the file can be executed, `0x1`, otherwise `0`.
Examples:
* `{read := true : AccessRight}.flags = 4`
* `{read := true, write := true : AccessRight}.flags = 6`
* `{read := true, execution := true : AccessRight}.flags = 5`
-/
def AccessRight.flags (acc : AccessRight) : UInt32 :=
let r : UInt32 := if acc.read then 0x4 else 0
let w : UInt32 := if acc.write then 0x2 else 0
let x : UInt32 := if acc.execution then 0x1 else 0
r.lor <| w.lor x
IO.FileRight
structure
/--
POSIX-style file permissions that describe access rights for a file's owner, members of its
assigned group, and all others.
-/
structure FileRight where
/-- The owner's permissions to access the file. -/
user : AccessRight := {}
/-- The assigned group's permissions to access the file. -/
group : AccessRight := {}
/-- The permissions that all others have to access the file. -/
other : AccessRight := {}
IO.FileRight.flags
definition
(acc : FileRight) : UInt32
/--
Converts POSIX-style file permissions to their numeric representation, with three bits each for the
owner's permissions, the group's permissions, and others' permissions.
-/
def FileRight.flags (acc : FileRight) : UInt32 :=
let u : UInt32 := acc.user.flags.shiftLeft 6
let g : UInt32 := acc.group.flags.shiftLeft 3
let o : UInt32 := acc.other.flags
u.lor <| g.lor o
IO.Prim.setAccessRights
opaque
(filename : @& FilePath) (mode : UInt32) : IO Unit
@[extern "lean_chmod"] opaque Prim.setAccessRights (filename : @& FilePath) (mode : UInt32) : IO Unit
IO.setAccessRights
definition
(filename : FilePath) (mode : FileRight) : IO Unit
/--
Sets the POSIX-style permissions for a file.
-/
def setAccessRights (filename : FilePath) (mode : FileRight) : IO Unit :=
Prim.setAccessRights filename mode.flags
IO.Ref
abbrev
(α : Type)
/--
Mutable reference cells that contain values of type `α`. These cells can read from and mutated in
the `IO` monad.
-/
abbrev Ref (α : Type) := ST.Ref IO.RealWorld α
IO.instMonadLiftSTRealWorldBaseIO
instance
: MonadLift (ST IO.RealWorld) BaseIO
IO.mkRef
definition
(a : α) : BaseIO (IO.Ref α)
IO.CancelToken
structure
/--
Mutable cell that can be passed around for purposes of cooperative task cancellation: request
cancellation with `CancelToken.set` and check for it with `CancelToken.isSet`.
This is a more flexible alternative to `Task.cancel` as the token can be shared between multiple
tasks.
-/
structure CancelToken where
private ref : IO.Ref Bool
deriving Nonempty
IO.instNonemptyCancelToken
instance
: Nonempty✝ (@IO.CancelToken✝)
Nonempty
IO.CancelToken.new
definition
: BaseIO CancelToken
/-- Creates a new cancellation token. -/
def new : BaseIO CancelToken :=
CancelToken.mkCancelToken.mk <$> IO.mkRef false
IO.CancelToken.set
definition
(tk : CancelToken) : BaseIO Unit
IO.CancelToken.isSet
definition
(tk : CancelToken) : BaseIO Bool
/-- Checks whether the cancellation token has been activated. -/
def isSet (tk : CancelToken) : BaseIO Bool :=
tk.ref.get
IO.FS.Stream.ofHandle
definition
(h : Handle) : Stream
/--
Creates a Lean stream from a file handle. Each stream operation is implemented by the corresponding
file handle operation.
-/
@[export lean_stream_of_handle]
def ofHandle (h : Handle) : Stream where
flush := Handle.flush h
read := Handle.read h
write := Handle.write h
getLine := Handle.getLine h
putStr := Handle.putStr h
isTty := Handle.isTty h
IO.FS.Stream.Buffer
structure
IO.FS.Stream.ofBuffer
definition
(r : Ref Buffer) : Stream
/--
Creates a stream from a mutable reference to a buffer.
The resulting stream simulates a file, mutating the contents of the reference in response to writes
and reading from it in response to reads. These streams can be used with `IO.withStdin`,
`IO.setStdin`, and the corresponding operators for standard output and standard error to redirect
input and output.
-/
def ofBuffer (r : Ref Buffer) : Stream where
flush := pure ()
read := fun n => r.modifyGet fun b =>
let data := b.data.extract b.pos (b.pos + n.toNat)
(data, { b with pos := b.pos + data.size })
write := fun data => r.modify fun b =>
-- set `exact` to `false` so that repeatedly writing to the stream does not impose quadratic run time
{ b with data := data.copySlice 0 b.data b.pos data.size false, pos := b.pos + data.size }
getLine := do
let buf ← r.modifyGet fun b =>
let pos := match b.data.findIdx? (start := b.pos) fun u => u == 0 || u = '\n'.toNat.toUInt8 with
-- include '\n', but not '\0'
| some pos => if b.data.get! pos == 0 then pos else pos + 1
| none => b.data.size
(b.data.extract b.pos pos, { b with pos := pos })
match String.fromUTF8? buf with
| somesome str => pure str
| none => throw (.userError "invalid UTF-8")
putStr := fun s => r.modify fun b =>
let data := s.toUTF8
{ b with data := data.copySlice 0 b.data b.pos data.size false, pos := b.pos + data.size }
isTty := pure false
termPrintln!__
definition
: Lean.ParserDescr✝
Runtime.markMultiThreaded
definition
(a : α) : BaseIO α
/--
Marks given value and its object graph closure as multi-threaded if currently
marked single-threaded. This will make reference counter updates atomic and
thus more costly. It can still be useful to do eagerly when the value will be
shared between threads later anyway and there is available time budget to mark
it now. -/
@[extern "lean_runtime_mark_multi_threaded"]
def Runtime.markMultiThreaded (a : α) : BaseIO α := return a
Runtime.markPersistent
definition
(a : α) : BaseIO α
/--
Marks given value and its object graph closure as persistent. This will remove
reference counter updates but prevent the closure from being deallocated until
the end of the process! It can still be useful to do eagerly when the value
will be marked persistent later anyway and there is available time budget to
mark it now or it would be unnecessarily marked multi-threaded in between.
This function is only safe to use on objects (in the full closure) which are
not used concurrently or which are already persistent.
-/
@[extern "lean_runtime_mark_persistent"]
unsafe def Runtime.markPersistent (a : α) : BaseIO α := return a
Runtime.forget
definition
(a : α) : BaseIO Unit
/--
Discards the passed owned reference. This leads to `a` any any object reachable from it never being
freed. This can be a useful optimization for eliding deallocation time of big object graphs that are
kept alive close to the end of the process anyway (in which case calling `Runtime.markPersistent`
would be similarly costly to deallocation). It is still considered a safe operation as it cannot
lead to undefined behavior.
-/
@[extern "lean_runtime_forget"]
def Runtime.forget (a : α) : BaseIO Unit := return