Installation

Install on Windows:

winget install -e --id Diskuv.dk

or Apple/Silicon:

sudo curl -o /usr/local/bin/dk https://diskuv.com/a/dk-exe/2.4.202506160116-signed/dk-darwin_arm64
sudo chmod +x /usr/local/bin/dk

or Apple/Intel:

sudo curl -o /usr/local/bin/dk https://diskuv.com/a/dk-exe/2.4.202506160116-signed/dk-darwin_x86_64
sudo chmod +x /usr/local/bin/dk

or Linux with glibc and libstdc++ (Debian, Ubuntu, etc. but not Alpine):

sudo curl -o /usr/local/bin/dk https://diskuv.com/a/dk-exe/2.4.202506160116-signed/dk-linux_x86_64
sudo chmod +x /usr/local/bin/dk
[ -x /usr/bin/dnf ] && sudo dnf install -y libstdc++

Say: Make it so

Copy and paste in your Terminal (PowerShell on Windows) and wait for a minute until you see Make it so.:

dk -g dune -U "Tr1Stdlib_V414Io.StdIo.print_endline {|Make it so.|}" Run

Run it again and it will be fairly quick:

dk -g dune -U "Tr1Stdlib_V414Io.StdIo.print_endline {|Make it so.|}" Run

Congratulations, you have run your first script!

Let's breakdown exactly what happened ...

The -g dune was the build generator option. dk is technically a meta-build system like CMake, where build files are generated for a specific build system. Within the broader OCaml ecosystem, "Dune" is the conventional build system that compiles OCaml source code into executables. With -g dune, dk will create build files for Dune. The "Dune" build tool will then be invoked automatically to compile your scripts.

The -U <expression> introduces the unit expression: Tr1Stdlib_V414Io.StdIo.print_endline {|Make it so.|}. Expressions are the basic OCaml language construct where we can call functions. If you are unfamiliar with expressions, the chapter OCaml Programming: Correct + Efficient + Beautiful: Expressions is a great introduction. The "unit" in "unit expression" is the type of the expression. The "unit" type corresponds closely to the "void" return type in Java, C# and C. We'll be returning to the expression shortly.

The Run command line argument is the name of the tool to invoke. The Run tool naturally runs whatever you said on the command line. There are other tools which we'll see in the next section.

Let's get back to the unit expression: Tr1Stdlib_V414Io.StdIo.print_endline {|Make it so.|}. You may be asking why would we have an expression that returns "unit" ... that is, it doesn't return anything? The answer is that the expression produces useful results (in this example, we'll be printing to the console screen), even without returning a value.

Tr1Stdlib_V414Io.StdIo.print_endline {|Make it so.|} is a function call expression [¹]. We had to add double-quotes around the function call so that your Unix shell, Windows PowerShell or Command Prompt knows that the whole Tr1Stdlib_V414Io.StdIo.print_endline {|Make it so.|} is the command line argument to -U.

Let's compare the OCaml function call expression to the equivalent function call statement in Java:

System.out.println ("Make it so.");

OCaml is unusual among other languages in that you don't use parentheses separate the function reference from the function argument. In OCaml, we separate each function arguments from the function reference using whitespace.

Let's start with the function arguments. The first and only argument to Tr1Stdlib_V414Io.StdIo.print_endline was {|Make it so.|}. The {|Make it so.|} is a string. The unusual {| begin and |} end tokens tell OCaml to interpret the intervening characters Make it so. as a "literal" string: that is, a string that has no escape characters. Normally we would use double-quotes like "Make it so." which is interpreted as a regular string. However, because we already used double-quotes to support Unix shell / Windows PowerShell / Command Prompt, we use the literal string syntax instead.

Now let's move back to the function reference. The Java function reference System.out.println has the same behavior as Tr1Stdlib_V414Io.StdIo.print_endline does in dk: It prints a string and ends it with a newline.

The first dot-separated term of the function reference Tr1Stdlib_V414Io.StdIo.print_endline was Tr1Stdlib_V414Io. That is called the library id. You can find a description of all the dk libraries at dkcoder-libraries(7).

The next term is StdIo, which is a module defined in the Tr1Stdlib_V414Io library. A module, at least for our purposes in this section, is a namespace for functions. There can be many modules, and the modules may be nested with a dot separator (.) just like Java packages.

The last term, the last term is print_endline, which is the function we want to call. Unfortunately at this time the dk documentation is not rendering a description of the function, but when it does it will be here.

Cross-compiling

Now copy and paste and wait for several minutes (the first time only!):

dk -g dune -S "
    module Http = DkNet_Std.Http
    module Uri = Tr1Uri_Std.Uri
    let print_endline = Tr1Stdlib_V414Io.StdIo.print_endline
 " -U "
    print_endline @@
    Lwt_main.run @@
    Http.fetch_url ~max_sz:4096 @@
    Uri.of_string {|https://jigsaw.w3.org/HTTP/h-content-md5.html|}
 " -O ReleaseSmall Exe

What happened? You created standalone executables for Linux and Windows and Android, all with one command, and if you are on macOS you also created standalone macOS executables.

Go ahead and find the standalone executable for your machine, and then run it:

  • target/ZzZz_Zz.Adhoc-android_arm32v7a
  • target/ZzZz_Zz.Adhoc-android_arm64v8a
  • target/ZzZz_Zz.Adhoc-android_x86_64
  • target/ZzZz_Zz.Adhoc-darwin_arm64
  • target/ZzZz_Zz.Adhoc-darwin_x86_64
  • target/ZzZz_Zz.Adhoc-linux_x86
  • target/ZzZz_Zz.Adhoc-linux_x86_64
  • target/ZzZz_Zz.Adhoc-windows_x86_64.exe
  • target/ZzZz_Zz.Adhoc-windows_x86.exe

In this section we introduced a few more command line options and arguments.

The last argument Exe is the tool. In the last section we had used the Run tool, but in this section we used the Exe tool because we wanted to generate executables for different operating systems.

The -O ReleaseSmall option tells dk that we want to optimize the generated executable for a small file size and to exclude most debug information.

The -S <structure> option introduces the structure (aka. contents) of a module. Before we had mentioned to consider a "module" as just a namespace for functions. Certainly the module can have functions, but it can also have what are called "module aliases".

Let consider the first module alias: module Http = DkNet_Std.Http. What that alias does is let us use Http instead of the longer DkNet_Std.Http for the remainder of the module (which includes the unit expression -U <expression>).

But the normal use of a module is to define values, especially functions. In our example above the function print_endline was defined using the module binding:

let print_endline = Tr1Stdlib_V414Io.StdIo.print_endline

Bindings are very similar to module aliases, except they operate on values (including functions) rather than modules. So print_endline can be used instead of the fully-qualifed function reference Tr1Stdlib_V414Io.StdIo.print_endline for the remainder of the module.

The only thing we haven't explained is what are all those @@ doing in the function calls? @@ is an operator that places the terms on the right hand side of the expression in parentheses.

So:

print_endline @@
Lwt_main.run @@
Http.fetch_url ~max_sz:4096 @@
Uri.of_string {|https://jigsaw.w3.org/HTTP/h-content-md5.html|}

is really:

print_endline (
   Lwt_main.run (
      Http.fetch_url ~max_sz:4096 (
         Uri.of_string {|https://jigsaw.w3.org/HTTP/h-content-md5.html|})))

Using a script file

Let's save the last script to a file. Copy and paste the following (notice we use Run rather than -O ReleaseSmall Exe):

dk -g dune -S "
    module Http = DkNet_Std.Http
    module Uri = Tr1Uri_Std.Uri
    let print_endline = Tr1Stdlib_V414Io.StdIo.print_endline
 " -U "
    print_endline @@
    Lwt_main.run @@
    Http.fetch_url ~max_sz:4096 @@
    Uri.of_string {|https://jigsaw.w3.org/HTTP/h-content-md5.html|}
 " Run

You will see that it prints the contents for you (scroll up a bit):

module Http = DkNet_Std.Http
module Uri = Tr1Uri_Std.Uri

let print_endline = Tr1Stdlib_V414Io.StdIo.print_endline

let () =
    print_endline @@ Lwt_main.run
    @@ Http.fetch_url ~max_sz:4096
    @@ Uri.of_string {|https://jigsaw.w3.org/HTTP/h-content-md5.html|}

Put that into a file; let's name it myfirstscript.ml.

Then run it with:

dk -g dune -s myfirstscript.ml Run

Congratulations, you saved and ran your first script.

In the last section we had described how the -S <structure> option introduce the structure (contents) of a module. Here we use -s <structure-file> option instead, which just reads the module from the file rather than using command line option as-is.

Next Steps

You can: