# Differentiate between mutable variables , immutable variables , functions and higher\_order functions

**URL:** <https://discuss.ocaml.org/t/differentiate-between-mutable-variables-immutable-variables-functions-and-higher-order-functions/9785>\
**Category:** Learning\
**Created:** [May 5, 2022, 11:08am UTC](https://discuss.ocaml.org/t/differentiate-between-mutable-variables-immutable-variables-functions-and-higher-order-functions/9785 "2022-05-05T11:08:36Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![devosalain](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/devosalain/32/3325_2.png) [@devosalain](https://discuss.ocaml.org/u/devosalain)\
**Post date:** [May 5, 2022, 11:08am UTC](https://discuss.ocaml.org/t/differentiate-between-mutable-variables-immutable-variables-functions-and-higher-order-functions/9785/1 "2022-05-05T11:08:36Z")

</div>

My problem is rather simple. Reading my own code i have difficulty is seeing in an eye if something is a mutable variable an immutable variable a function or a higher order function when it is used.  
Any ideas on given them different names, “a standard ?”, or places them in different blocks/modules ?

---

<div class="post-metadata">

**Author:** ![glennsl](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/glennsl/32/142_2.png) [@glennsl](https://discuss.ocaml.org/u/glennsl)\
**Post date:** [May 5, 2022, 11:12am UTC](https://discuss.ocaml.org/t/differentiate-between-mutable-variables-immutable-variables-functions-and-higher-order-functions/9785/2 "2022-05-05T11:12:02Z")

</div>

Why do you need to differentiate them?

---

<div class="post-metadata">

**Author:** ![devosalain](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/devosalain/32/3325_2.png) [@devosalain](https://discuss.ocaml.org/u/devosalain)\
**Post date:** [May 5, 2022, 11:13am UTC](https://discuss.ocaml.org/t/differentiate-between-mutable-variables-immutable-variables-functions-and-higher-order-functions/9785/3 "2022-05-05T11:13:20Z")

</div>

To improve readability of the program.

---

<div class="post-metadata">

**Author:** ![glennsl](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/glennsl/32/142_2.png) [@glennsl](https://discuss.ocaml.org/u/glennsl)\
**Post date:** [May 5, 2022, 11:16am UTC](https://discuss.ocaml.org/t/differentiate-between-mutable-variables-immutable-variables-functions-and-higher-order-functions/9785/4 "2022-05-05T11:16:49Z")

</div>

What exactly do you mean by “readability”, and how does differentiating between these specific aspects improve it?

---

<div class="post-metadata">

**Author:** ![glennsl](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/glennsl/32/142_2.png) [@glennsl](https://discuss.ocaml.org/u/glennsl)\
**Post date:** [May 5, 2022, 11:28am UTC](https://discuss.ocaml.org/t/differentiate-between-mutable-variables-immutable-variables-functions-and-higher-order-functions/9785/5 "2022-05-05T11:28:56Z")

</div>

It seems likely that you’re overusing mutable variables, and the solution then is usually not to differentiate but to avoid it, using a number of different techniques depending on scenario, or narrow the scope to the point where it’s no longer a problem.

But if for some reason you actually do need mutation in a larger scope, you might want to consider hiding the variable and using `get_x` and `set_x` functions to access it.

I cannot think of any reason for needing to differentiate between “ordinary” functions and higher-order function, however. Perhaps you could illustrate the problem with an example?

---

<div class="post-metadata">

**Author:** ![devosalain](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/devosalain/32/3325_2.png) [@devosalain](https://discuss.ocaml.org/u/devosalain)\
**Post date:** [May 5, 2022, 12:07pm UTC](https://discuss.ocaml.org/t/differentiate-between-mutable-variables-immutable-variables-functions-and-higher-order-functions/9785/6 "2022-05-05T12:07:40Z")

</div>

Offcourse. A simple example system programming. The command lsblk in linux gives partition information, i rewrote it for freebsd. This is the code but i find my own code hard to read…  
The code starts with 4 “associative arrays”

```auto
let () =
    let nam2dev = ref [("", "")] in
    let dev2nam = ref [("", "")] in
    let dev2mount = ref [("", "")] in
    let dev2ro = ref [("", "")] in
    let read_process_lines (command:string) : (string list) =
        let lines = ref [] in
        let in_channel = Unix.open_process_in command in
        (try
           while true do
             lines := input_line in_channel :: !lines
           done
         with End_of_file -> ignore (Unix.close_process_in in_channel));
        List.rev !lines
    in
    let rec mylookupkv (sk:string) (alist:(string*string)list) : string =
        match alist with
        | [] -> ""
        | (key, value) :: tail -> if sk = key then value else mylookupkv sk tail
    in
    let regexpos regex line : int =
        try Str.search_forward regex line 0 with Not_found -> -1
    in
    let spacereg = Str.regexp " +" in
    let glabelline line =
        let lst = Str.split spacereg line in
        let n = "/dev/" ^ List.hd lst in
        let d = "/dev/" ^ List.nth lst 1 in
        nam2dev := (n, d) :: !nam2dev;
        dev2nam := (d, n) :: !dev2nam
    in
    let mountline line =
        let lst = Str.split spacereg line in
        let w = List.hd lst in
        let t = List.nth lst 2 in
        let v = mylookupkv w !nam2dev in
        let w2 = if v <> "" then mylookupkv w !nam2dev else w in
        dev2mount := (w2, t) :: !dev2mount;
        let roreg = Str.regexp "read-only" in
        let pos = regexpos roreg line in
        if pos <> -1 then dev2ro := (w2, "ro") :: !dev2ro
        else dev2ro := (w2, "rw") :: !dev2ro
    in
    let id_to_command (cid:string) : string = "ps -p " ^ cid ^ " -o command | sed 1d | sort -u" in
    let ntfsline line =
        let lst = Str.split spacereg line in
        let id = List.hd lst in
        let command = id_to_command id in
        let pstxt = read_process_lines command in
        let lst2 = Str.split spacereg (List.hd pstxt) in
        let d = List.nth lst2 3 in
        let m = List.nth lst2 4 in
        let () =
            let () = dev2ro := (d, "?") :: !dev2ro in
            let () = dev2mount := (d, m) :: !dev2mount in
            ()
        in
        ()
    in
    let eqline (x:bool) line =
        let len = String.length line in
        if x then String.sub line 2 (len - 2) else line
    in
    let ismin (x:bool) (line:string) :string = if x then "<Free>" else line in
    let mountval (x:string) (d:string) : string = if x = "" then "-" else mylookupkv d !dev2mount in
    let roval (x:string) (d:string) : string = if x = "" then "-" else mylookupkv d !dev2ro in
    let padme (x:int) (s:string) : string =
        let numspaces = x - String.length s in
        let spacesref = ref "" in
        for i = 0 to numspaces do
          spacesref := " " ^ !spacesref
        done;
        let sum = s ^ !spacesref in
        sum
    in
    let gpartline (line : string) =
        if String.length line > 0 then
          let c = line.[0] in
          let qline = eqline (c = '=') line in
          let asplit = Str.split spacereg qline in
          let el2 = List.nth asplit 2 in
          let el3orfree = List.nth asplit 3 in
          let el3 = if el3orfree = "free" then "-" else el3orfree in
          let d = "/dev/" ^ el2 in
          let aname = ":" ^ ismin (el2 = "-") d in
          let amount = ":" ^ mountval (mylookupkv d !dev2mount) d in
          let atype = ":" ^ el3 in
          let ro = ":" ^ roval (mylookupkv d !dev2ro) d in
          let openreg = Str.regexp "(" in
          let closereg = Str.regexp ")" in
          let openpos = regexpos openreg qline in
          let closepos = regexpos closereg qline in
          if openpos <> -1 then
            if closepos <> -1 then
              let asize = String.sub qline openpos (closepos - openpos + 1) in
              let output =
                  padme 19 aname ^ padme 13 asize ^ padme 20 atype
                  ^ padme 20 amount ^ padme 11 ro ^ "\n"
              in
              print_string output
            else ()
          else ()
    in
    let glabeltxt_to_kv (lines : string list) = List.map glabelline lines in
    let glabeltxt : string list = read_process_lines "glabel status | awk '{if (NR!=1) print $1,$3}'" in
    let mounttxt_to_kv (lines : string list) = List.map mountline lines in
    let mounttxt : string list = read_process_lines "mount" in
    let ntfstxt_to_kv (lines : string list) = List.map ntfsline lines in
    let ntfstxt : string list = read_process_lines "pgrep ntfs-3g" in
    let gparttxt_to_kv (lines : string list) = List.map gpartline lines in
    let gparttxt : string list = read_process_lines "gpart show -p" in
    let (_ : unit list) = glabeltxt_to_kv glabeltxt in
    let (_ : unit list) = mounttxt_to_kv mounttxt in
    let (_ : unit list) = ntfstxt_to_kv ntfstxt in
    let (_ : unit list) = gparttxt_to_kv gparttxt in
    ()

```

---

<div class="post-metadata">

**Author:** ![glennsl](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/glennsl/32/142_2.png) [@glennsl](https://discuss.ocaml.org/u/glennsl)\
**Post date:** [May 5, 2022, 12:44pm UTC](https://discuss.ocaml.org/t/differentiate-between-mutable-variables-immutable-variables-functions-and-higher-order-functions/9785/7 "2022-05-05T12:44:31Z")

</div>

It’s still not obvious to me from this in what way differentiating between higher-order functions and ordinary functions would improve readability. But there are a lot of other things that could.

Let me try to illustrate my point about the overuse of mutable variables above, as one example of how the underlying problem is in how you structure the program.

`nam2dev` is a ref cell (mutable variable) that is mutated in exactly one location, inside `glabelline`, which in turn is also just used in one location, inside `glabeltxt_to_kv`. So why does the entire program have access to mutate it?

What if `glabelline` just returned the `(nam, dev)` pair for each line, and `glabeltxt_to_kv` used those to construct and return the `nam2dev` and `dev2nam` lists, instead of a `unit list` that is just thrown away? Then there would be no need to differentiate.

```auto
    let glabelline line =
        let lst = Str.split spacereg line in
        let nam = "/dev/" ^ List.hd lst in
        let dev = "/dev/" ^ List.nth lst 1 in
        (nam, dev)
    in
    let glabeltxt_to_kv (lines : string list) = 
      let nam2dev = List.map glabelline lines in
      let dev2name = List.map (fun (n, d) -> (d, n)) nam2dev in
      (nam2dev, dev2nam)
    in
    ...

```

---

<div class="post-metadata">

**Author:** ![vlaviron](https://avatars.discourse-cdn.com/v4/letter/v/ec9cab/32.png) [@vlaviron](https://discuss.ocaml.org/u/vlaviron)\
**Post date:** [May 5, 2022, 1:45pm UTC](https://discuss.ocaml.org/t/differentiate-between-mutable-variables-immutable-variables-functions-and-higher-order-functions/9785/8 "2022-05-05T13:45:59Z")

</div>

I’d like to point out that strictly speaking, OCaml has no mutable variables. It has mutable data structures, like arrays, reference cells, and so on, but when you define `let x = ref 0 in ...` you define an immutable variable, `x`, bound to a mutable reference cell. Sometimes the compiler will manage to optimise the cell away, but sometimes not.

Some tips that help readability:

- Don’t forget comments. Typically, when several operations are performed in a sequence, it eventually makes sense to try to split these into sections and use comments to separate the sections.
- Don’t use references for things that are initialised, then never changed again (as said above). For things that are built incrementally, it’s still better to only use a reference when building, and only expose an immutable value once it’s initialised.
- Don’t use `List.nth` with constant indices. If you’re expecting a fixed number of elements, you can use `let [x; y; z] = my_list in ...`, if you only have a minimum number of elements you can use `let x :: y :: z :: rest = my_list in ...`. Note that these will get you warnings about partial matches, which is a good sign that you’re doing something a bit strange; you can work around that using:

```ocaml
let (x, y, z) =
  match mylist with
  | [x; y; z] -> x, y, z
  | _ -> failwith "<some useful error message>"
in
...

```

- Look a bit at the standard library for useful functions. `List.map` with a function returning `unit` is better written as `List.iter`, the `padme` function could use `String.make numspaces ' '`, the `Printf` and `format` modules have very useful functions for creating strings in a more readable way than `^` (look in particular at the `sprintf` function).
- Use variant types. The option type could prove useful, but for instance the `ismin` function would read better as:

```ocaml
type device_or_free = Free | Device of string
let ismin (dev : device_or_free) =
  match dev with
  | Free -> "<Free>"
  | Device d -> d
in
...
let d =
  if el2 = "-" then Free else Device ("/dev/" ^ el2)
in
... ismin d ...

```

- Don’t be afraid of higher order functions. For example, currently your `read_process_lines` function only returns the lines. But in all cases, the lines will be handled one by one anyway, so you might as well streamline the process:

```ocaml
let handle_process_lines ~(handler: string -> unit) (command : string) : unit =
  let in_channel = Unix.open_process_in command in
  let rec loop () =
    let line = input_line in_channel in
    handler line;
    ()
  in
  try loop () with End_of_file -> ignore (Unix.close_process_in in_channel)
in
...
let () = handle_process_lines ~handler:glabelline "glabel status | awk '{if (NR!=1) print $1,$3}'" in
...

```

(Note that if you want to combine it with the suggestion to return the `nam2dev` and `dev2nam` lists in this step, you’ll need to tweak a bit the code to make it look more like a `fold` function)

Overall, it looks like you’ve translated mechanically code from another language into OCaml. This is never going to end up very readable. It’s a good way to get initial working code, but if you want to be able to maintain it you should spend time to make it look more like idiomatic OCaml (incrementally, you don’t need to rewrite everything at once).

---

<div class="post-metadata">

**Author:** ![devosalain](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/devosalain/32/3325_2.png) [@devosalain](https://discuss.ocaml.org/u/devosalain)\
**Post date:** [May 6, 2022, 1:14am UTC](https://discuss.ocaml.org/t/differentiate-between-mutable-variables-immutable-variables-functions-and-higher-order-functions/9785/9 "2022-05-06T01:14:57Z")

</div>

With all due respect. I look at things as ada.  
This means here a function, its clear input & output & variables.  
It’s just now breaking my head looking at the same way in a different thing.

---

<div class="post-metadata">

**Author:** ![jbeckford](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/jbeckford/32/3027_2.png) [@jbeckford](https://discuss.ocaml.org/u/jbeckford)\
**Post date:** [May 6, 2022, 5:13am UTC](https://discuss.ocaml.org/t/differentiate-between-mutable-variables-immutable-variables-functions-and-higher-order-functions/9785/10 "2022-05-06T05:13:41Z")

</div>

Take what I say with a grain of salt … in relative terms I’m an OCaml newbie at ~1 year.

The previous comments make sense. But even still I don’t think I would be able to read your code with those suggested modifications. _Your code is missing types; right now everything has been collapsed into strings_. I’ve taken a stab at introducing types … and noticed in a couple places I couldn’t get the type checker to pass (if the compiler can’t understand it, I doubt a human can read it).

Summary of changes:

- Some mutations have become derivatives. For example, when `dev2mount` is “changed” I introduce a new variable `dev2mount'`.
- I’ve captured the types into modules for four reasons:
  1. A module signature lets me hide the underlying representation of `Nam.t` and `Dev.t` (etc.) so that `Nam.t` can’t be compared to a `Dev.t`. Then I can let the type checker catch semantic errors, and it should also help understanding the code.
  2. Those types start simple (`string` in your case) but inevitably they would get more complex. I would replace them with records (ex. `module Dev = struct type t = { inode: int; mutable name: string } (*...*) end`) without having to refactor much code.
  3. A module keeps related things together in the code, so I typically don’t find it hard to analyze where , for example, a mutable field `type t = { mutable name: string}` is getting changed.
  4. It is annoying to consume two record types that have the same record name in the same module. Qualifying the type with a module name avoids the problem.

- Once I introduced the types `Nam.t`, `Nam2dev.t`, etc. I just changed the code so that it passed the type checker. To do that I had to switch a few of your `List.map` into `List.fold` as suggested elsewhere.

From Ada to OCaml:

- `out` parameters are just return arguments. In the code below I just used tuples, but in real code I will often use a new single type (ex. `module Result = struct type t = { val1: int; val2: string } end`) and just let the caller access the output fields by name.

```ocaml
(** {1 Item} *)

module type ITEM = sig
  type t
  val empty : t
  val create : string -> t
  val show : t -> string
end
module ItemBasic = struct
  type t = string
  let create s = s
  let empty = ""
  let show s = s
end
module Nam : ITEM = ItemBasic
module Dev : ITEM = ItemBasic
module Mount : ITEM = ItemBasic
module Ro : ITEM = ItemBasic

(** {1 Item Maps} *)

module type ITEMMAP = sig
  type key
  type value
  type t
  val empty : t
  val of_list : (key * value) list -> t
  val push : t -> key -> value -> t
  val mylookupkv : key -> t -> value
end
module ItemMapBasic (Key: ITEM) (Value: ITEM) = struct
  type key = Key.t
  type value = Value.t
  type t = (Key.t*Value.t) list
  let empty = []
  let of_list lst = lst
  let push lst n d = (n, d) :: lst  
  let rec mylookupkv (sk:Key.t) (alist:(Key.t*Value.t)list) : Value.t =
    match alist with
    | [] -> Value.empty
    | (key, value) :: tail -> if sk = key then value else mylookupkv sk tail
end
module Nam2dev : ITEMMAP with type key = Nam.t and type value = Dev.t = struct
  include ItemMapBasic (Nam) (Dev)
end
module Dev2nam : ITEMMAP with type key = Dev.t and type value = Nam.t = struct
  include ItemMapBasic (Dev) (Nam)
end
module Dev2mount : ITEMMAP with type key = Dev.t and type value = Mount.t = struct
  include ItemMapBasic (Dev) (Mount)
end
module Dev2ro : ITEMMAP with type key = Dev.t and type value = Ro.t = struct
  include ItemMapBasic (Dev) (Ro)
end

let () =
    let read_process_lines (command:string) : (string list) =
        let lines = ref [] in
        let in_channel = Unix.open_process_in command in
        (try
           while true do
             lines := input_line in_channel :: !lines
           done
         with End_of_file -> ignore (Unix.close_process_in in_channel));
        List.rev !lines
    in
    let regexpos regex line : int =
        try Str.search_forward regex line 0 with Not_found -> -1
    in
    let spacereg = Str.regexp " +" in
    let glabelline line =
        let lst = Str.split spacereg line in
        let n = Nam.create @@ "/dev/" ^ List.hd lst in
        let d = Dev.create @@ "/dev/" ^ List.nth lst 1 in
        n, d
    in
    let mountline ~nam2dev ~dev2mount ~dev2ro line =
        let lst = Str.split spacereg line in
        let w = List.hd lst in
        let t = Mount.create @@ List.nth lst 2 in
        let v = Nam2dev.mylookupkv (Nam.create w) nam2dev in
        let w2 = if v <> Dev.empty then Nam2dev.mylookupkv (Nam.create w) nam2dev else Dev.create w in
        let dev2mount' = Dev2mount.push dev2mount w2 t in
        let roreg = Str.regexp "read-only" in
        let pos = regexpos roreg line in
        let dev2ro' = if pos <> -1 then Dev2ro.push dev2ro w2 (Ro.create "ro")
        else Dev2ro.push dev2ro w2 (Ro.create "rw") in
        dev2mount', dev2ro'
    in
    let id_to_command (cid:string) : string = "ps -p " ^ cid ^ " -o command | sed 1d | sort -u" in
    let ntfsline ~dev2ro ~dev2mount line =
        let lst = Str.split spacereg line in
        let id = List.hd lst in
        let command = id_to_command id in
        let pstxt = read_process_lines command in
        let lst2 = Str.split spacereg (List.hd pstxt) in
        let d = Dev.create @@ List.nth lst2 3 in
        let m = Mount.create @@ List.nth lst2 4 in
        let dev2ro' = Dev2ro.push dev2ro d (Ro.create "?") in
        let dev2mount' = Dev2mount.push dev2mount d m in
        (dev2mount', dev2ro')
    in
    let eqline (x:bool) line =
        let len = String.length line in
        if x then String.sub line 2 (len - 2) else line
    in
    let ismin (x:bool) (line:string) :string = if x then "<Free>" else line in
    let mountval ~dev2mount (x:string) (d:Dev.t) : Mount.t = if x = "" then Mount.create "-" else Dev2mount.mylookupkv d dev2mount in
    let roval ~dev2ro (x:string) (d:Dev.t) : Ro.t = if x = "" then Ro.create "-" else Dev2ro.mylookupkv d dev2ro in
    let padme (x:int) (s:string) : string =
        let numspaces = x - String.length s in
        let spacesref = ref "" in
        for i = 0 to numspaces do
          spacesref := " " ^ !spacesref
        done;
        let sum = s ^ !spacesref in
        sum
    in
    let print_gpartline ~dev2ro ~dev2mount (line : string) =
        if String.length line > 0 then
          let c = line.[0] in
          let qline = eqline (c = '=') line in
          let asplit = Str.split spacereg qline in
          let el2 = List.nth asplit 2 in
          let el3orfree = List.nth asplit 3 in
          let el3 = if el3orfree = "free" then "-" else el3orfree in
          let d = "/dev/" ^ el2 in
          let aname = ":" ^ ismin (el2 = "-") d in
          (* THIS LOOKS FISHY ... CAN'T GET THE TYPES TO LINE UP ... REPLACING IT! *)
          (* let amount = ":" ^ Mount.show (mountval ~dev2mount d (Dev2mount.MYLOOKUPKV d dev2mount)) in *)
          let amount = ":" ^ Mount.show (Dev2mount.mylookupkv (Dev.create d) dev2mount) in
          let atype = ":" ^ el3 in
          (* THIS LOOKS FISHY ... CAN'T GET THE TYPES TO LINE UP ... REPLACING IT! *)
          (* let ro = ":" ^ Ro.show (roval ~dev2ro d (Dev2ro.mylookupkv d dev2ro)) in *)
          let ro = ":" ^ Ro.show (Dev2ro.mylookupkv (Dev.create d) dev2ro) in
          let openreg = Str.regexp "(" in
          let closereg = Str.regexp ")" in
          let openpos = regexpos openreg qline in
          let closepos = regexpos closereg qline in
          if openpos <> -1 then
            if closepos <> -1 then
              let asize = String.sub qline openpos (closepos - openpos + 1) in
              let output =
                  padme 19 aname ^ padme 13 asize ^ padme 20 atype
                  ^ padme 20 amount ^ padme 11 ro ^ "\n"
              in
              print_string output
            else ()
          else ()
    in
    let glabeltxt_to_nam2dev (lines : string list) =
      let nam_dev_lst : (Nam.t * Dev.t) list = List.map glabelline lines in
      Nam2dev.of_list nam_dev_lst
    in
    let glabeltxt : string list = read_process_lines "glabel status | awk '{if (NR!=1) print $1,$3}'" in
    let mounttxt_to_kv ~nam2dev (lines : string list) =
      List.fold_right
        (fun line (dev2mount, dev2ro) -> mountline ~nam2dev ~dev2mount ~dev2ro line)
        lines
        (Dev2mount.empty, Dev2ro.empty)
    in
    let mounttxt : string list = read_process_lines "mount" in
    let ntfstxt_to_kv ~dev2mount ~dev2ro (lines : string list) =
      List.fold_right
        (fun line (dev2mount, dev2ro) -> ntfsline ~dev2mount ~dev2ro line)
        lines
        (dev2mount, dev2ro)
    in
    let ntfstxt : string list = read_process_lines "pgrep ntfs-3g" in
    let print_gparttxt ~dev2mount ~dev2ro (lines : string list) =
      List.iter
        (fun line -> print_gpartline ~dev2mount ~dev2ro line)
        lines
    in
    let gparttxt : string list = read_process_lines "gpart show -p" in
    let nam2dev = glabeltxt_to_nam2dev glabeltxt in
    let dev2mount, dev2ro = mounttxt_to_kv ~nam2dev mounttxt in
    let dev2mount', dev2ro' = ntfstxt_to_kv ~dev2mount ~dev2ro ntfstxt in
    print_gparttxt ~dev2mount:dev2mount' ~dev2ro:dev2ro' gparttxt

```
