File Paths in Standard ML

Here’s the translation of the Go code example to Standard ML, formatted in Markdown suitable for Hugo:

Our first example demonstrates how to work with file paths in Standard ML. We’ll use the OS.Path structure, which provides functions to manipulate file paths in a portable way across different operating systems.

(* File paths example *)

(* We'll use the OS.Path structure for path manipulation *)
open OS.Path;

(* Join should be used to construct paths in a portable way *)
val p = concat ["dir1", "dir2", "filename"];
val _ = print ("p: " ^ p ^ "\n");

(* concat will also normalize paths by removing superfluous separators *)
val _ = print (concat ["dir1//", "filename"] ^ "\n");
val _ = print (concat ["dir1/../dir1", "filename"] ^ "\n");

(* dir and file can be used to split a path to the directory and the file *)
val _ = print ("Dir(p): " ^ dir p ^ "\n");
val _ = print ("Base(p): " ^ file p ^ "\n");

(* We can check whether a path is absolute *)
val _ = print (Bool.toString (isAbsolute "dir/file") ^ "\n");
val _ = print (Bool.toString (isAbsolute "/dir/file") ^ "\n");

val filename = "config.json";

(* Some file names have extensions following a dot *)
(* We can split the extension out of such names with splitBaseExt *)
val {base, ext} = splitBaseExt filename;
val _ = print (case ext of SOME e => e ^ "\n" | NONE => "No extension\n");

(* To find the file's name with the extension removed *)
val _ = print (base ^ "\n");

(* mkRelative finds a relative path between a base and a target *)
val rel = mkRelative {path = "a/b/t/file", relativeTo = "a/b"};
val _ = print (rel ^ "\n");

val rel = mkRelative {path = "a/c/t/file", relativeTo = "a/b"};
val _ = print (rel ^ "\n");

To run this program, save it to a file (e.g., file_paths.sml) and use your Standard ML interpreter. For example, if you’re using SML/NJ:

$ sml file_paths.sml

This will produce output similar to:

p: dir1/dir2/filename
dir1/filename
dir1/filename
Dir(p): dir1/dir2
Base(p): filename
false
true
.json
config
t/file
../c/t/file

Note that the exact output might vary depending on your operating system and Standard ML implementation.

In this example, we’ve used the OS.Path structure, which provides functions for manipulating file paths in a portable way. The concat function is used to join path components, similar to filepath.Join in the original example. The dir and file functions are used to split a path into its directory and file components.

Standard ML doesn’t have a direct equivalent to filepath.IsAbs, so we’ve used isAbsolute from OS.Path. Similarly, we’ve used splitBaseExt to get the file extension and base name, and mkRelative to find relative paths.

Remember that while this code aims to be portable, the exact behavior might still vary slightly between different operating systems and Standard ML implementations.