Stateful Programming in Idris Edwin Brady ( ecb10@st-andrews.ac.uk ) University of St Andrews, Scotland, UK @edwinbrady Dagstuhl, 30th January 2017 1 / 22
Idris is a pure functional language with dependent types . Some goals: Reduce the cost of writing correct software Be accessible to software developers and practitioners In this talk: Writing precise APIs with State Example: Sockets and Concurrency 2 / 22
Socket States 3 / 22
Socket Programming (in C) Creating a server int socket(int domain, int type, int protocol); int bind(int socket, const struct sockaddr *address, socklen_t address_len); int listen(int socket, int backlog); int accept(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len); int connect(int socket, struct sockaddr *address, socklen_t address_len) /* ... */ 4 / 22
Socket Programming (in C) Which int is which? (Sockets) int socket(int domain, int type, int protocol); int bind(int socket, const struct sockaddr *address, socklen_t address_len); int listen(int socket, int backlog); int accept(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len); int connect(int socket, struct sockaddr *address, socklen_t address_len) /* ... */ 5 / 22
Socket Programming (in C) Which int is which? (Errors) int socket(int domain, int type, int protocol); int bind(int socket, const struct sockaddr *address, socklen_t address_len); int listen(int socket, int backlog); int accept(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len); int connect(int socket, struct sockaddr *address, socklen_t address_len) /* ... */ 6 / 22
Socket Programming (in Haskell) Creating a server socket :: Family -> SocketType -> ProtocolNumber -> IO Socket bind :: Socket -> SockAddr -> IO () listen :: Socket -> Int -> IO () accept :: Socket -> IO (Socket, SockAddr) connect :: Socket -> SockAddr -> IO () 7 / 22
Towards better APIs Challenge: How can we write a descriptive API for sockets? Wishlist: 8 / 22
Towards better APIs Challenge: How can we write a descriptive API for sockets? Wishlist: Capture the states of the socket 8 / 22
Towards better APIs Challenge: How can we write a descriptive API for sockets? Wishlist: Capture the states of the socket Describe when operations are valid (i.e. preconditions) 8 / 22
Towards better APIs Challenge: How can we write a descriptive API for sockets? Wishlist: Capture the states of the socket Describe when operations are valid (i.e. preconditions) Describe how operations affect the environment (i.e. postconditions) 8 / 22
Towards better APIs Challenge: How can we write a descriptive API for sockets? Wishlist: Capture the states of the socket Describe when operations are valid (i.e. preconditions) Describe how operations affect the environment (i.e. postconditions) Interoperate with other stateful APIs 8 / 22
Towards better APIs Challenge: How can we write a descriptive API for sockets? Wishlist: Capture the states of the socket Describe when operations are valid (i.e. preconditions) Describe how operations affect the environment (i.e. postconditions) Interoperate with other stateful APIs Remain Readable 8 / 22
Towards better APIs Challenge: How can we write a descriptive API for sockets? Wishlist: Capture the states of the socket Describe when operations are valid (i.e. preconditions) Describe how operations affect the environment (i.e. postconditions) Interoperate with other stateful APIs Remain Readable Lead to helpful error messages 8 / 22
Towards better APIs Challenge: How can we write a descriptive API for sockets? Wishlist: Capture the states of the socket Describe when operations are valid (i.e. preconditions) Describe how operations affect the environment (i.e. postconditions) Interoperate with other stateful APIs Remain Readable Lead to helpful error messages Inspiration: Separation logic , Ynot , Indexed Monads , Linear Types . . . 8 / 22
Example state machine: Doors 9 / 22
State in Idris: ST The type of stateful programs ST : (m : Type -> Type) -- Underlying monad -> (ty : Type) -- Result type -> List (Action ty) -- State transitions -- may depend on the result -> Type 10 / 22
State in Idris: ST Example A Door Program doorProg : Door m => ST m () [] doorProg = do d <- newDoor ringBell d OK <- doorOpen d | Jammed => delete d doorClose d delete d 11 / 22
State in Idris: ST Example Ringing the bell ringBell : (d : Var) -> ST m () [d ::: DoorType DoorClosed] 12 / 22
State in Idris: ST Example Ringing the bell ringBell : (d : Var) -> ST m () [d ::: DoorType DoorClosed] Closing a door doorClose : (d : Var) -> ST m () [d ::: DoorType DoorOpen :-> DoorType DoorClosed] 12 / 22
State in Idris: ST Example Opening a door (almost. . . ) doorOpen : (d : Var) -> ST m () [d ::: DoorType DoorClosed :-> DoorType DoorOpen] 13 / 22
State in Idris: ST Example Opening a door (with possible failure) data Result = OK | Jammed doorOpen : (d : Var) -> ST m Result [d ::: DoorType DoorClosed :-> (\res => DoorType (case res of Jammed => DoorClosed OK => DoorOpen))] 14 / 22
State in Idris: ST Example Creating a door newDoor : ST m Var [Add (\d => [d ::: DoorType DoorClosed])] 15 / 22
State in Idris: ST Example General idea: Define interfaces (closely related to type classes ) for operations in ST: Abstract away underlying resource types ( DoorType here) Implement interface for different contexts Use only needed interfaces 16 / 22
State in Idris: ST Example General idea: Define interfaces (closely related to type classes ) for operations in ST: Abstract away underlying resource types ( DoorType here) Implement interface for different contexts Use only needed interfaces The Door Interface interface Door (m : Type -> Type) where DoorType : DoorState -> Type newDoor : ... ringBell : ... doorOpen : ... doorClose : ... 16 / 22
State in Idris: ST Example A Door Program, with IO doorProg : (ConsoleIO m, Door m) => ST m () [] doorProg = do d <- newDoor ringBell d OK <- doorOpen d | Jammed => delete d lift (putStrLn "The Door is Open") doorClose d delete d 17 / 22
Demonstration : Networking and Concurrency 18 / 22
Internal details: STrans The type of stateful programs STrans : (m : Type -> Type) -- Underlying monad -> (ty : Type) -- Result type -> Context -- Input states -> (ty -> Context) -- Output states -- may depend on the result -> Type 19 / 22
Internal details: STrans Reading and writing state get : (lbl : Var) -> {auto prf : InState lbl ty ctxt} -> STrans m ty ctxt (const ctxt) put : (lbl : Var) -> {auto prf : InState lbl ty ctxt} -> (val : ty’) -> STrans m () ctxt (const (updateCtxt ctxt prf ty’)) 20 / 22
Internal details: STrans Returning a value pure : (result : val) -> STrans m val (out_fn result) out_fn 21 / 22
https://www.tinyurl.com/typedd 22 / 22
Recommend
More recommend