# Extend Arg with Arg.File?

**URL:** https://discuss.ocaml.org/t/extend-arg-with-arg-file/5999
**Category:** Learning
**Created:** [June 18, 2020, 9:55am UTC](https://discuss.ocaml.org/t/extend-arg-with-arg-file/5999 "2020-06-18T09:55:30Z")
**Posts on this page:** 6
**Page:** 1

<div class="post-metadata">

### Author: ![dromas](https://avatars.discourse-cdn.com/v4/letter/d/58f4c7/32.png) [@dromas](https://discuss.ocaml.org/u/dromas)
#### Post date: [June 18, 2020, 9:55am UTC](https://discuss.ocaml.org/t/extend-arg-with-arg-file/5999/1 "2020-06-18T09:55:30Z")

</div>

Hello,

is it possible to extend Arg with Arg.File?

### My use case

My (imaginary) tool has two options: `-o` and `-v`.

`-o` expects an output filename   
`-v` turns verbose logging on.

So this is the spec:

```
[
  "-o",
  Arg.String (fun fname -> outfile := fname),
  "output filename";

  "-v",
  Arg.Set verbose,
  "verbose mode";
]

```

### Problem

When a user mistakenly omits the filename after `-o` and invokes the tool with `mytool -o -v` the output is written into a file “-v”.  
Most likely, this wasn’t the intended behavior.

### My current workarround

Whenever a string argument is supposed to be a filename I make sure it dosn’t start with a dash:

```
  "-o",
  Arg.String (fun fname ->
      match fname with
      | "" ->
	    eprintf "-o: filename cannot be empty string";
		exit 1

      | "-" ->
	    outfile := fname (* stdin or stdout *)

      | s when s.[0] = '-' ->
	    eprintf "-o: filename missing before %S\n" fname;
		exit 1

      | _ ->
	    outfile := fname
    ),
    "outfile filename";

```

### Problem with my workaround

I need this over and over again and would like to handle it with something like

```
"-o",
Arg.File (fun fname -> outfile := fname),
"output filename";

```

which implements the workaround internally.

## My attempted solution

```
module Arg = struct
  include Arg

  type spec += File of (string -> unit)
end

```

### Problem with my attempted solution

It doesn’t compile :-/

```
Error: Type definition spec is not extensible

```

### Questions

- Is there a better way to handle the situation than the work around described above?
- Whould it be appropriate to add `Arg.File` to the Stdlib?

---

<div class="post-metadata">

### Author: ![nojb](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/nojb/32/519_2.png) [@nojb](https://discuss.ocaml.org/u/nojb)
#### Post date: [June 18, 2020, 10:13am UTC](https://discuss.ocaml.org/t/extend-arg-with-arg-file/5999/2 "2020-06-18T10:13:41Z")

</div>

> [@dromas](#):
>
> ### Problem
> 
> When a user mistakenly omits the filename after `-o` and invokes the tool with `mytool -o -v` the output is written into a file “-v”.  
> Most likely, this wasn’t the intended behavior.

Note that this is the standard command-line behaviour in the Unix world (try it with your favourite C compiler).

> [@dromas](#):
>
> ### Problem with my attempted solution
> 
> It doesn’t compile :-/
> 
> ```auto
> Error: Type definition spec is not extensible
> 
> ```

Only extensible sum types can be, well, extended. Ordinary sum types cannot be extended. Note that it wouldn’t make sense to extend `Arg.spec`, as the module `Arg` would not know what to do with the new constructor.

Best wishes,  
Nicolás

---

<div class="post-metadata">

### Author: ![dromas](https://avatars.discourse-cdn.com/v4/letter/d/58f4c7/32.png) [@dromas](https://discuss.ocaml.org/u/dromas)
#### Post date: [June 18, 2020, 10:54am UTC](https://discuss.ocaml.org/t/extend-arg-with-arg-file/5999/3 "2020-06-18T10:54:58Z")

</div>

> Note that this is the standard command-line behaviour in the Unix world (try it with your favourite C compiler).

You’re right.  
I thought filenames with leading dashes must be “escaped” with `--` in  
the Unix world, e.g. `gcc -o -- -v file.c`. Turns out this is not the  
case and thus shouldn’t be “fixed” in the Stdlib.

> Only extensible sum types can be, well, extended.  
> Ordinary sum types cannot be extended.

I skimed over [https://sites.google.com/site/ocamlopen/](https://sites.google.com/site/ocamlopen/) and  
misinterpreted the “…” as arbitrary constructors.  
Thus I concluded all types are extensible since 4.02.  
I was wrong again.

> Note that it wouldn’t make sense to extend `Arg.spec`, as the module `Arg` would not know what to do with the new constructor.

Yes, this was just the first step.

---

<div class="post-metadata">

### Author: ![rbardou](https://avatars.discourse-cdn.com/v4/letter/r/a88e4f/32.png) [@rbardou](https://discuss.ocaml.org/u/rbardou)
#### Post date: [June 18, 2020, 3:23pm UTC](https://discuss.ocaml.org/t/extend-arg-with-arg-file/5999/4 "2020-06-18T15:23:32Z")

</div>

Instead of trying to extend the type, you can define a function to share your code:

```auto
let arg_file file_ref =
  Arg.String (fun fname ->
      match fname with
      | "" ->
	    eprintf "-o: filename cannot be empty string";
		exit 1

      | "-" ->
	    file_ref := fname (* stdin or stdout *)

      | s when s.[0] = '-' ->
	    eprintf "-o: filename missing before %S\n" fname;
		exit 1

      | _ ->
	    file_ref := fname
    )
in

Arg.spec [
  "-i", arg_file infile, "<FILE> input file";
  "-o", arg_file outfile, "<FILE> output file";
]

```

Although that would prevent you from passing filenames which start with a dash, so I would not do that.

---

<div class="post-metadata">

### Author: ![Chet\_Murthy](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/chet_murthy/32/1501_2.png) [@Chet\_Murthy](https://discuss.ocaml.org/u/Chet_Murthy)
#### Post date: [June 18, 2020, 3:27pm UTC](https://discuss.ocaml.org/t/extend-arg-with-arg-file/5999/5 "2020-06-18T15:27:06Z")

</div>

This isn’t really a solution, but … have you thought about learning to use the excellent, excellent (did I mention it’s excellent) `cmdliner` package? It was sort of meant for this sort of thing – adding behaviour to the processing of args in a modular way, I mean.

---

<div class="post-metadata">

### Author: ![dromas](https://avatars.discourse-cdn.com/v4/letter/d/58f4c7/32.png) [@dromas](https://discuss.ocaml.org/u/dromas)
#### Post date: [June 19, 2020, 8:32am UTC](https://discuss.ocaml.org/t/extend-arg-with-arg-file/5999/6 "2020-06-19T08:32:12Z")

</div>

> [@rbardou](#):
>
> Instead of trying to extend the type, you can define a function to share your code:

Thanks, this is viable, to0.

> [@rbardou](#):
>
> Although that would prevent you from passing filenames which start with a dash, so I would not do that.

This should still be possible with `--`.

> [@Chet\_Murthy](#):
>
> have you thought about learning to use the excellent, excellent (did I mention it’s excellent) `cmdliner` package?

Not really - thanks for the suggestion. I usually try to avoid non-stdlib packages if the functionality takes just a couple of lines.
