Clausal Prolog — TCP Module (tcp)¶
Overview¶
The tcp module provides predicates for TCP client/server socket operations,
wrapping Python's socket module. Socket handles are opaque Python objects —
use them with send, receive, and close.
-import_from(py.tcp, [connect, send, receive, close])
echo_client(HOST, PORT, MESSAGE, RESPONSE) <- (
connect(HOST, PORT, SOCKET),
send(SOCKET, MESSAGE),
receive(SOCKET, RESPONSE),
close(SOCKET)
)
Or via module import:
-import_module(py.tcp)
main <- (
py.tcp.connect("localhost", 8080, S),
py.tcp.send(S, "hello"),
py.tcp.receive(S, REPLY),
++print(REPLY),
py.tcp.close(S)
)
Import¶
Client predicates¶
| Predicate | Mode | Description |
|---|---|---|
connect(Host, Port, Socket) |
+Host, +Port, -Socket |
connect to TCP server, bind opaque socket handle |
A network failure raises (ruled 2026-10-02; it used to fail): a host that
does not resolve is existence_error(source_sink, Host) (Scryer's
socket_client_open/3 term), a refused connection
system_error(connection_refused), an unreachable host
system_error(host_unreachable), a timeout resource_error(timeout), a
permission refusal permission_error(open, source_sink, Host). listen/3
on a port in use is system_error(address_in_use); send/2 on a broken
connection system_error(broken_pipe) or system_error(connection_reset).
Server predicates¶
| Predicate | Mode | Description |
|---|---|---|
listen(Host, Port, ServerSocket) |
+Host, +Port, -ServerSocket |
Create listening socket with SO_REUSEADDR. Port 0 → ephemeral. |
accept(ServerSocket, ClientSocket) |
+Server, -Client |
accept incoming connection. Blocks until a client connects. |
listen("0.0.0.0", 8080, SERVER),
accept(SERVER, CLIENT),
receive(CLIENT, DATA),
send(CLIENT, DATA), # echo back
close(CLIENT),
close(SERVER)
Data transfer¶
| Predicate | Mode | Description |
|---|---|---|
send(Socket, Data) |
+Socket, +Data |
send string (UTF-8) or bytes via sendall() |
receive(Socket, Data) |
+Socket, -Data |
receive up to 4096 bytes, decode UTF-8 |
receive(Socket, BufferSize, Data) |
+Socket, +BufSize, -Data |
receive with custom buffer size |
receive returns a string if the data is valid UTF-8, or raw bytes if
decoding fails. Fails if the connection is closed (no data received).
send(SOCKET, "Hello, server!")
receive(SOCKET, RESPONSE)
# Custom buffer size
receive(SOCKET, 65536, LARGE_DATA)
Socket management¶
| Predicate | Mode | Description |
|---|---|---|
close(Socket) |
+Socket |
close socket. Always succeeds (even on already-closed sockets). |
set_timeout(Socket, Seconds) |
+Socket, +Seconds |
Set socket timeout (float). A later operation that times out raises resource_error(timeout). |
set_timeout(SOCKET, 5.0), # 5-second timeout
receive(SOCKET, DATA) # fails if no data within 5 seconds
Examples
Simple TCP client¶
-import_from(py.tcp, [connect, send, receive, close])
tcp_request(HOST, PORT, REQUEST, RESPONSE) <- (
connect(HOST, PORT, S),
send(S, REQUEST),
receive(S, RESPONSE),
close(S)
)
Echo server (single client)¶
Gotchas¶
- Sockets are impure — socket operations have side effects and do not
backtrack cleanly. If a
sendsucceeds but a later goal fails, the data has already been sent. acceptblocks — it waits for a connection. Useset_timeouton the server socket to limit the wait time.- No automatic cleanup — always
closesockets explicitly. Python's garbage collector will eventually close them, but relying on GC is bad practice for network resources. - Port 0 in
listenlets the OS pick an ephemeral port — useful for tests.
See also: HTTP — higher-level HTTP requests (no sockets needed) ·
Process — shell commands and subprocess execution ·
Python Interop — ++() for advanced socket operations.