Path

let separator (): String

\\ on Windows, / everywhere else.

let is_absolute (p: String): Bool

Rooted: /x anywhere, and on Windows also C:/x and a //server UNC path. C:x (a drive-RELATIVE path) is deliberately not absolute; it means "the current directory on drive C", which is a relative path with a drive attached.

let clean (p: String): String

Removes duplicate separators, . segments, and resolvable .. segments, and drops a trailing separator. An empty result is ., matching Go's filepath.Clean. The empty string is not a path, and . is the one it means.

let join_all (parts: Array[String]): String

--- join ---

Empty segments are skipped, so join("", "b") is b rather than /b; joining nothing to something must not turn a relative path absolute.

A later ABSOLUTE segment does not discard the earlier ones: join("a", "/b") is a/b, matching Go's filepath.Join. Python goes the other way and lets /b win, which is convenient exactly until a caller passes user input as the second argument and silently escapes the directory they meant to stay inside.

let join (a: String) (b: String): String

Joins two path segments with the platform separator, cleaning the result.

A trailing separator on a or a leading one on b does not produce a doubled separator. An absolute b wins: joining /etc onto /home/x gives /etc, matching every other language's join and the shell's own reading of an absolute path.

let basename (p: String): String

The last component. "" is ., a bare root is itself, and a trailing separator is ignored (a/b/ is b).

let dirname (p: String): String

Everything before the last component, cleaned. A path with no separator is ., the directory it is in.

let extension (p: String): String

The extension of the last segment, without its dot, or empty when there is none.

A leading dot does not start an extension: .gitignore is a name, not an extension, and returns nothing. archive.tar.gz gives gz -- the LAST dot wins, so stem and extension always recombine into the original name.

let stem (p: String): String

The last component without its extension.

let with_extension (p: String) (ext: String): String

p with a different extension, given without a dot. An empty ext removes it.