unison/IO.u
2019-07-17 00:04:03 -04:00

367 lines
12 KiB
Plaintext

type Either a b = Left a | Right b
type Optional a = None | Some a
-- This is linked to definitions that are considered tests
unique[e6dca08b40458b03ca1660cfbdaecaa7279b42d18257898b5fd1c34596aac36f] type
IsTest = IsTest
links.isTest = IsTest.IsTest
-- Handles are unique identifiers.
-- The implementation of IO in the runtime will supply Haskell
-- file handles and map those to Unison handles.
-- A pure implementation of I/O might use some kind of pure supply
-- of unique IDs instead.
unique[d4597403ec40fd4fbee57c62b8096f9c3d382dff01f20108546fe3530a927e86] type
io.Handle = Handle Text
-- Ditto for sockets
unique[e1d94401fde8b2546d6dfc54e93f11e6a9285a7ea765d3255da19122a42715d3] type
io.Socket = Socket Text
-- Builtin handles: standard in, out, error
use io Error Mode Handle IO Socket ThreadId HostName FilePath EpochTime
BufferMode SeekMode ServiceName
use io.Handle Handle
namespace io where
stdin : Handle
stdin = Handle "stdin"
stdout : Handle
stdout = Handle "stdout"
stderr : Handle
stderr = Handle "stderr"
-- Throw an I/O error on the left as an effect in `IO`
rethrow : (Either io.Error a) -> {IO} a
rethrow x = case x of
Either.Left e -> io.IO.throw e
Either.Right a -> a
-- Print a line to the standard output
printLine : Text ->{IO} ()
printLine t =
putText stdout t
putText stdout "\n"
-- Read a line from the standard input
readLine : '{IO} Text
readLine = '(getLine stdin)
-- Built-ins
-- Open a named file in the given mode, yielding an open file handle
openFile : FilePath -> Mode ->{IO} Handle
openFile f m = rethrow (io.IO.openFile_ f m)
-- Close an open file handle
closeFile : Handle ->{IO} ()
closeFile f = rethrow (io.IO.closeFile_ f)
-- Check whether a file handle has reached the end of the file
isFileEOF : Handle ->{IO} Boolean
isFileEOF h = rethrow (io.IO.isFileEOF_ h)
-- Check whether a file handle is open
isFileOpen : Handle ->{IO} Boolean
isFileOpen h = rethrow (io.IO.isFileOpen_ h)
-- Get a line of text from a text file handle
getLine : Handle ->{IO} Text
getLine h = rethrow (io.IO.getLine_ h)
-- Get the entire contents of a file as a single block of text
getText : Handle ->{IO} Text
getText h = rethrow (io.IO.getText_ h)
-- Write some text to a file
putText : Handle -> Text ->{IO} ()
putText h t = rethrow (io.IO.putText_ h t)
-- Get epoch system time
systemTime : '{IO} EpochTime
systemTime = '(rethrow (io.IO.systemTime_))
-- Does the file handle support `seek`?
isSeekable : Handle -> {IO} Boolean
isSeekable h = rethrow (io.IO.isSeekable_ h)
-- Seek to a position in a file handle
seek : Handle -> SeekMode -> Int ->{IO} ()
seek h m i = rethrow (io.IO.seek_ h m i)
-- Ask for the position of a file handle
position : Handle ->{IO} Int
position h = rethrow (io.IO.position_ h)
-- Get the buffer mode of a file handle
getBuffering : Handle ->{IO} (Optional BufferMode)
getBuffering h = rethrow (io.IO.getBuffering_ h)
-- Set the buffer mode for a file handle
setBuffering : Handle -> Optional BufferMode ->{IO} ()
setBuffering h bm = rethrow (io.IO.setBuffering_ h bm)
-- Get the path to a temporary directory managed by the operating system
getTemporaryDirectory : '{IO} FilePath
getTemporaryDirectory = '(rethrow (io.IO.getTemporaryDirectory_))
-- Get the current working directory
getCurrentDirectory : '{IO} FilePath
getCurrentDirectory = '(rethrow (io.IO.getCurrentDirectory_))
-- Set the current working directory
setCurrentDirectory : FilePath -> {IO} ()
setCurrentDirectory d = rethrow (io.IO.setCurrentDirectory_ d)
-- List the contents of a directory
directoryContents : FilePath -> {IO} [FilePath]
directoryContents d = rethrow (io.IO.directoryContents_ d)
-- Check if a path exists
fileExists : FilePath -> {IO} Boolean
fileExists d = rethrow (io.IO.fileExists_ d)
-- Check if a path is a directory
isDirectory : FilePath -> {IO} Boolean
isDirectory d = rethrow (io.IO.isDirectory_ d)
-- Create a directory at the given path, including parent directories
createDirectory : FilePath -> {IO} ()
createDirectory d = rethrow (io.IO.createDirectory_ d)
-- Remove the directory at the given path
removeDirectory : FilePath -> {IO} ()
removeDirectory d = rethrow (io.IO.removeDirectory_ d)
-- Move a directory from one path to another
renameDirectory : FilePath -> FilePath -> {IO} ()
renameDirectory from to = rethrow (io.IO.renameDirectory_ from to)
-- Remove a file from the file system
removeFile : FilePath -> {IO} ()
removeFile d = rethrow (io.IO.removeFile_ d)
-- Move a file from one path to another
renameFile : FilePath -> FilePath -> {IO} ()
renameFile from to = rethrow (io.IO.renameFile_ from to)
-- Get the timestamp of a file
getFileTimestamp : FilePath -> {IO} EpochTime
getFileTimestamp d = rethrow (io.IO.getFileTimestamp_ d)
-- Get the size of a file in bytes
getFileSize : FilePath -> {IO} Nat
getFileSize d = rethrow (io.IO.getFileSize_ d)
-- Create a socket bound to the given local port/service.
-- If a hostname is not given, this will use any available host.
serverSocket : Optional HostName -> ServiceName -> {IO} Socket
serverSocket host service = rethrow (io.IO.serverSocket_ host service)
-- Start listening for connections on the given socket.
listen : Socket -> {IO} ()
listen s = rethrow (io.IO.listen_ s)
-- Create a socket connected to the given remote address.
clientSocket : HostName -> ServiceName -> {IO} Socket
clientSocket host service = rethrow (io.IO.clientSocket_ host service)
-- Close a socket and all connections to it.
closeSocket : Socket -> {IO} ()
closeSocket s = rethrow (io.IO.closeSocket_ s)
-- Accept a connection on a socket.
-- Returns a socket that can send and receive data on a new connection
accept : Socket -> {IO} Socket
accept s = rethrow (io.IO.accept_ s)
-- Send some bytes to a socket.
send : Socket -> Bytes -> {IO} ()
send s bs = rethrow (io.IO.send_ s bs)
-- Read the specified number of bytes from a socket.
receive : Socket -> Nat ->{IO} (Optional Bytes)
receive s n = rethrow (io.IO.receive_ s n)
-- Fork a new thread.
fork : '{IO} a -> {IO} ThreadId
fork a = rethrow (io.IO.fork_ a)
-- Kill a running thread.
kill : ThreadId -> {IO} ()
kill t = rethrow (io.IO.kill_ t)
-- Suspend the current thread for a number of microseconds.
delay : Nat -> {IO} ()
delay n = rethrow (io.IO.delay_ n)
-- Safely acquire and release a resource
bracket : '{IO} a -> (a ->{IO} b) -> (a ->{IO} c) -> {IO} c
bracket acquire release what = rethrow (io.IO.bracket_ acquire release what)
-- Run the given computation, and if it throws an error
-- handle the error with the given handler.
-- catch : '{IO} a -> (io.Error ->{IO} a) ->{IO} a
-- catch c h =
-- k io = case io of
-- { IO.throw e } -> h e
-- x -> x
-- handle k in c
-- IO Modes from the Haskell API
type io.Mode = Read | Write | Append | ReadWrite
-- IO error types from the Haskell API
unique[bb57f367a3740d4a1608b9e0eee14fd744ec9e368f1529550cb436ef56c0b268] type
io.ErrorType
= AlreadyExists
| NoSuchThing
| ResourceBusy
| ResourceExhausted
| EOF
| IllegalOperation
| PermissionDenied
| UserError
unique[b5c578f0a9977ed54a5a12b580dc6b0b2ba37bc3f517f48d1b3285a7f3e8c6bc] type
io.ErrorLocation = ErrorLocation Text
unique[e6ca048b6bf540f93617c0ef9506afcbb490427a9581a01d51ffad39cdf2c554] type
io.ErrorDescription = ErrorDescription Text
unique[d5d61b0a65f1d448dbdeed8af688f0bdbab6b3f775400da370eb5bfc34e428d5] type
io.FilePath = FilePath Text
type io.Error = Error io.ErrorType Text
unique[cad7ab802bd143f0b674155c9caf18dde7145d16867a02659534d7bb01a5e287] type
io.SeekMode = Absolute | Relative | FromEnd
-- If the buffer size is not specified,
-- use an implementation-specific size.
unique[e65de145a461a771de93d6c7885acae28552d77f8ae460bc8bf5de6f2a15ff77] type
io.BufferMode = Line | Block (Optional Nat)
unique[e1f48f31982a720ae895c0bf4e6ea9a950f5c00d3a73101ad31e63461b7beded] type
io.EpochTime = EpochTime Nat
-- Either a host name e.g., "unisonweb.org" or a numeric host address
-- string consisting of a dotted decimal IPv4 address
-- e.g., "192.168.0.1".
unique[c7279b501764751edc66f1f7b532e68354fc4704c9eb1ed201f01c894cdd86f4] type
io.HostName = HostName Text
-- For example a port number like "8080"
unique[ee4ff0bda526b0513e4c7b7387b39811ce57938ddb31a77fdb0ff00ee2717c33] type
io.ServiceName = ServiceName Text
unique[a38186de35c9fcd29d2b359b2148f9f890732413d91575af39d025fcded67e89] type
io.ThreadId = ThreadId Text
ability io.IO where
-- Basic file IO
openFile_ : io.FilePath -> io.Mode -> (Either io.Error io.Handle)
closeFile_ : io.Handle -> (Either io.Error ())
isFileEOF_ : io.Handle -> (Either io.Error Boolean)
isFileOpen_ : io.Handle -> (Either io.Error Boolean)
-- Text input and output
--getChar : io.Handle -> Char
getLine_ : io.Handle -> (Either io.Error Text)
-- Get the entire contents of the file as text
getText_ : io.Handle -> (Either io.Error Text)
-- putChar : io.Handle -> Char -> ()
putText_ : io.Handle -> Text -> (Either io.Error ())
-- Throw an error as an `io.IO` effect
throw : io.Error -> a
-- File positioning
isSeekable_ : io.Handle -> (Either io.Error Boolean)
seek_ : io.Handle -> io.SeekMode -> Int -> (Either io.Error ())
position_ : io.Handle -> (Either io.Error Int)
-- File buffering
getBuffering_ : io.Handle -> Either io.Error (Optional io.BufferMode)
setBuffering_ : io.Handle -> Optional io.BufferMode -> (Either io.Error ())
-- Should we expose mutable arrays for byte buffering?
-- Inclined to say no, although that sounds a lot like
-- a decision to just be slow.
-- We'll need a byte buffer manipulation library in that case.
-- getBytes : io.Handle -> Nat -> Bytes
-- putBytes : io.Handle -> Bytes -> ()
-- getBytes : io.Handle -> Nat -> ByteArray -> Nat
-- putBytes : io.Handle -> Nat -> ByteArray -> ()
systemTime_ : (Either io.Error io.EpochTime)
-- File system operations
getTemporaryDirectory_ : (Either io.Error io.FilePath)
getCurrentDirectory_ : (Either io.Error io.FilePath)
setCurrentDirectory_ : io.FilePath -> (Either io.Error ())
directoryContents_ : io.FilePath -> Either io.Error [io.FilePath]
fileExists_ : io.FilePath -> (Either io.Error Boolean)
isDirectory_ : io.FilePath -> (Either io.Error Boolean)
createDirectory_ : io.FilePath -> (Either io.Error ())
removeDirectory_ : io.FilePath -> (Either io.Error ())
renameDirectory_ : io.FilePath -> io.FilePath -> (Either io.Error ())
removeFile_ : io.FilePath -> (Either io.Error ())
renameFile_ : io.FilePath -> io.FilePath -> (Either io.Error ())
getFileTimestamp_ : io.FilePath -> (Either io.Error io.EpochTime)
getFileSize_ : io.FilePath -> (Either io.Error Nat)
-- Simple TCP Networking
-- Create a socket bound to the given local address.
-- If a hostname is not given, this will use any available host.
serverSocket_ : Optional io.HostName ->
io.ServiceName -> (Either io.Error io.Socket)
-- Start listening for connections
listen_ : io.Socket -> (Either io.Error ())
-- Create a socket connected to the given remote address
clientSocket_ : io.HostName ->
io.ServiceName -> (Either io.Error io.Socket)
closeSocket_ : io.Socket -> (Either io.Error ())
--socketToio.Handle : Socket -> Mode -> (Either io.Error io.Handle)
--handleToSocket : io.Handle -> (Either io.Error Socket)
-- Accept a connection on a socket.
-- Returns a socket that can send and receive data on a new connection
accept_ : io.Socket -> (Either io.Error io.Socket)
-- Send some bytes to a socket.
send_ : io.Socket -> Bytes -> (Either io.Error ())
-- Read the spefified number of bytes from the socket.
receive_ : io.Socket -> Nat -> (Either io.Error (Optional Bytes))
-- scatter/gather mode network I/O
-- sendMany : Socket -> [Bytes] -> Int
-- Threading --
-- Fork a thread
fork_ : '{io.IO} a -> (Either io.Error io.ThreadId)
-- Kill a running thread
kill_ : io.ThreadId -> (Either io.Error ())
-- Suspend the current thread for a number of microseconds.
delay_ : Nat -> (Either io.Error ())
-- Safely acquire and release a resource
bracket_ : '{io.IO} a -> (a ->{io.IO} b) -> (a ->{io.IO} c) ->{io.IO} (Either io.Error c)