Internal Libraries
Internal Libraries are used to add complicated features from Python and JavaScript for users to use in N, you can import these using the import
keyword. Here are the libraries you can import.
Table of contents
FileIO
This is used for file input and output:
import FileIO
FileIO.write: (str, str) -> cmd[()]
: Takes in a path to a file and writes the data wanted into it, this does clear the file and will create it if it does not exist.
FileIO.write("test.txt", "test")!
FileIO.append: (str, str) -> cmd[()]
: Takes in a path to a file and writes the data wanted into it, this does not clear the file and will create it if it does not exist.
FileIO.append("test.txt", "test")!
FileIO.read: (str) -> cmd[maybe[str]]
: Takes in a path to a file and reads the data in it, returns it as a string if it does exist.
FileIO.read("test.txt")! |> default("")
FileIO.writeBytes: (str, list[int]) -> cmd[()]
: Takes in a path to a file and writes the data wanted into it, this does clear the file and will create it if it does not exist.
FileIO.writeBytes("test.txt", [32, 33, 34, 35])!
FileIO.appendBytes: (str, list[int]) -> cmd[()]
: Takes in a path to a file and writes the data wanted into it, this does not clear the file and will create it if it does not exist.
FileIO.appendBytes("test.txt", [32, 33, 34, 35])!
FileIO.readBytes: (str) -> cmd[maybe[list[int]]]
: Takes in a path to a file and reads the data in it, returns it as a list of bytes if it does exist.
FileIO.readBytes("test.txt")! |> default([])
FileIO.getFiles: (str) -> cmd[list[(bool, str)]]
: Requires the FILE_ALLOW
environment variable to be set to true
, takes in a path and returns all of the files/folders there, the bool
in the output indicates whether it is a file or not.
FileIO.getFiles("./")!
json
This is used for dealing with json
information:
import json
json.value
: An enum that is the main way that json
deals with info:
type pub value = object(map[str, value])
| number(float)
| string(str)
| boolean(bool)
| null
json.stringify: (json.value) -> str
: This takes a json value and turns it into a string for ease of use.
print(json.stringify(value))
json.parse: (str) -> json.value
: This takes in a string and will return a json.value
in an attempt to parse it, if it cannot parse it then it will return a json.null
print(json.parse('{ test: "test" }'))
json.parseSafe: (str) -> maybe[json.value]
: This takes in a string and will return a json.value
in an attempt to parse it, if it cannot parse it then it will return none
print(json.parseSafe("{ test: \"test\" }") |> default(json.string("invalid")))
request
Used for http related activities:
import request
request.request: (str, str, maybe[json.value], maybe[json.value]) -> cmd[{ code: int, response: str, return: json.value }]:
: This takes in the request type, url, headers, and data to send in that order and gives back the request reponse
request.post("github.com", "GET", none, none)! // Gets github.com's base page
request.createServer: int -> ( (str, str, json.value) -> cmd[{ responseCode: int, data: list[int], headers: map[str, str], mimetype: str }] ) -> cmd[()]
: This takes in a port to open to and a function which takes in a path, request type, and additional data and returns a response code, the data in bytes, headers, and the MIME type for the data given and opens a server on http://localhost:<PORTNUMBER>
request.createServer(
3000,
(path:str requestType:str, additionalData:str) -> cmd[{ responseCode: int, data: list[int], headers: map[str, str], mimetype: str }] {
return {
responseCode: 200,
data: [32, 33, 34, 35],
headers: mapFrom([]),
mimetype: "text/plain",
}
}
)!
SystemIO
Used for interacting with the console:
import SystemIO
SystemIO.inp: (str) -> cmd[str]
: Takes in a string and prints it out and awaits user input, once it is inputted it will give back the input
print("You said: " + SystemIO.inp("hello! ")!)
SystemIO.run: (str) -> cmd[bool]
: Requires the COMMAND_ALLOW
environment variable to be set to true
, takes in a string and runs the command in the terminal, returns true
if it was successful
SystemIO.run("echo hello")!
SystemIO.sendSTDOUT: [t] (t) -> cmd[t]
: Prints to the STDOUT, this does not append a newline to the end.
SystemIO.sendSTDOUT("Printing to the STDOUT\n")!
times
Used for stopping the program and getting the current time:
import times
times.sleep: (int) -> cmd[()]
: Takes in an integer and stops the thread it is in for that many milliseconds
times.sleep(1000)!
times.getTime: (()) -> cmd[float]
: Returns the current epoch time
times.getTime()!
websocket
Used for websocket related activities
import websocket
websocket.send: (str) -> cmd[result[(), int]]
: This is a type used for sending data in a websocket, if it does not have an error it will return an ok(())
otherwise it will return an err
with the error code it got.
alias send = str -> cmd[()]
websocket.close: () -> cmd[()]
: Currently unused.
websocket.user: { send: str -> cmd[()], disconnect: () -> cmd[()], ip: (int, int, int, int), uuid: str }
: Used as an individual user when hosting a websocket server.
alias user = {
send: str -> cmd[()]
disconnect: () -> cmd[()]
ip: (int, int, int, int)
uuid: str
}
websocket.connect: ({ onOpen: websocket.send -> cmd[bool], onMessage: websocket.send -> cmd[bool] }, str) -> cmd[maybe[str]]
: Connects to a websocket, onOpen
and onMessage
run in parallel and if either return true
it will exit out of both, the str
is an error message that my occur when connecting.
let websocketTest = websocket.connect({
onOpen: [send: websocket.send] -> cmd[bool] {
print("Open!")
let _ = send("hi")!
return false
}
onMessage: [send: websocket.send message: str] -> cmd[bool] {
print(message)
let _ = send("hello")!
return message == "hello"
}
}, "wss://echo.websocket.org")!
websocket.createServer: ({ onConnect: (websocket.user, str) -> cmd[bool], onMessage: (websocket.user, str) -> cmd[bool], onDisconnect: (websocket.user, { code: int, reson: str }) -> cmd[bool] }, int) -> cmd[()]
: Opens a websocket server on ws://localhost:<PORTNUMBER>
. Runs onConnect
when a user connects, onMessage
runs when a user sends a message, and onDisconnect
will run when a user disconnects, if either of these return true
then the websocket server will close.
websocket.createServer(
{
onConnect: (user:websocket.user, path:str) -> cmd[bool] {
print(user)
user.send("hello")!
return false
}
onMessage: (user:websocket.user, message:str) -> cmd[bool] {
print(message)
user.send(message)!
return false
}
onDisconnect: (user:websocket.user, exitData:maybe[{ code: int, reason:str }]) -> cmd[bool] { return false }
},
3000
)!
mutex
Mutex is a way of locking variables to avoid race conditions when implementing parallelism. You can pass in a value and get a mutex.locked
which you can then attempt to lock and get a mutex.unlocked
, which you can read or write to. While one thread has locked the mutex value, no other thread is able to access it until it unlocks it again. Mutex values do not need the mut
modifier applied to them.
mutex.new: [t] (t) -> mutex.locked[t]
: Creates a new mutex.locked
value from the value passed in.
let mutexValue = mutex.new(0)
mutex.tryAccess: [a, b] (mutex.unlocked[a] -> cmd[b], mutex.locked[a]) -> cmd[maybe[b]]
Attempts to access the mutex.locked
if it locked by another thread then it will continue without running the function. If it unlocked then it runs the function passed in and returns its result.
mutexValue
|> mutex.tryAccess(
(unlocked: mutex.unlocked[int]) -> cmd[()] {
// Do stuff with mutex
}
)!
mutex.access: [a, b] (mutex.unlocked[a] -> cmd[b], mutex.locked[a]) -> cmd[b]
Same as mutex.tryAccess
but it will wait for the value to be unlocked so it can run the function passed in before continuing.
mutexValue
|> mutex.access(
(unlocked: mutex.unlocked[int]) -> cmd[()] {
// Do stuff with mutex
}
)!
mutex.read: [t] (mutex.unlocked[t]) -> cmd[t]
Reads the value contained in a mutex.unlocked
.
let val = unlocked
|> mutex.read()!
mutex.write: [t] (t, mutex.unlocked[t]) -> cmd[t]
Writes a value to a mutex.unlocked
and returns the value.
unlocked
|> mutex.write(val + 1)!
Making your own
Whenever the import
keyword is called with a name that does not match one of these libraries, N will check for a python file of the same name in the directory, if it finds one it will check if it has a function called _values
, if this is successful then it will treat the file as a mapping for python.
The _values
function returns a dictionary of strings, which equate to the names of the functions, and types in the N syntax. These types are represented as such:
str
,int
,float
,bool
, andchar
are represented as their name wrapped in quotes ("str"
)char
s at runtime are represeted as one length strings
list[t]
isn_list_type.with_typevars([t])
map[k, v]
isn_map_type.with_typevars([k, v])
- Respresented as a dictionary during runtime
cmd[t]
isn_cmd_type.with_typevars([t])
- Represented as a
Cmd
object fromncmd.py
at runtime
- Represented as a
maybe[t]
isn_maybe_type.with_typevars([t])
- All enum values are represented as an
EnumValue
fromenums.py
- All enum values are represented as an
result[o, e]
isn_result_type.with_typevars([o, e])
,n_result_type
comes fromnative_types.py
module
isn_module_type.with_typevars([])
- Represented as a
NModuleWrapper
fromtype.py
at runtime
- Represented as a
- Records are represented as dictionaries during type checking and runtime
The _types
function is optional but if included must return a dictionary of strings to types. These types must be of the class NTypeVars
from type.py
These files may in the future be part of a Pypi library for ease of access.
Notes
- All of these are written in Python or JavaScript.
- Later there may be a way to write your own, but this is unlikely.
- The imported libraries are not records, but record-like.