# Is \[\] a type or not?

**URL:** <https://discuss.ocaml.org/t/is-a-type-or-not/8464>\
**Category:** Learning\
**Created:** [September 10, 2021, 3:08pm UTC](https://discuss.ocaml.org/t/is-a-type-or-not/8464 "2021-09-10T15:08:22Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![viritrilbia](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/viritrilbia/32/3083_2.png) [@viritrilbia](https://discuss.ocaml.org/u/viritrilbia)\
**Post date:** [September 10, 2021, 3:08pm UTC](https://discuss.ocaml.org/t/is-a-type-or-not/8464/1 "2021-09-10T15:08:22Z")

</div>

I wondered whether `[]` is a type, since it would be a natural notation for an empty polymorphic variant type. At first it doesn’t seem to be:

```ocaml
# let f (x : []) : [] = x;;
Error: Syntax error: type expected.

```

But this works:

```ocaml
# type foo = [];;
type foo = []
# let f (x : foo) : foo = x;;
val f : foo -> foo = <fun>

```

However, it doesn’t seem to be an empty type:

```ocaml
# let f (x : foo) = match x with _ -> .;;
Error: This match case could not be refuted.
       Here is an example of a value that would reach it: []

```

This error message suggests that `[]` is an element of the type `[]`, and indeed it seems to be, at least as long as the type was given a name:

```ocaml
# let y : foo = [];;
val y : foo = []
# let y : [] = [];;
Error: Syntax error
# let f (x : foo) = match x with [] -> 3;;
val f : foo -> int = <fun>

```

Normally `[]` denotes the empty list, so I thought maybe `[]` was “the type of empty lists”. But if so, these “lists” don’t seem to have anything to do with ordinary ones:

```ocaml
# 3 :: y;;
Error: This expression has type foo but an expression was expected of type
         int list
# let z : 'a list = [];;
val z : 'a list = []
# y = z;;
Error: This expression has type 'a list but an expression was expected of type
         foo

```

I am very confused. Is `[]` a type, as it seems to sort of be? If so, what type is it, and why does it only work in some places and not others? If not, what is going on?

---

<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:** [September 10, 2021, 3:16pm UTC](https://discuss.ocaml.org/t/is-a-type-or-not/8464/2 "2021-09-10T15:16:34Z")

</div>

It’s not exactly a type, it’s a type constructor. If I remember correctly, there are five special “keywords” that are parsed and typed as type constructors: `[]` and `(::)`, for the `list` type, `()`, for the `unit` type, and `true` and `false` for the `bool` type.  
Apart from this special parsing rules, there is nothing particular about these constructors and you can reuse them for custom types:

```ocaml
# type t = () | [] | (::) | true | false | Something;;
type t = () | [] | (::) | true | false | Something
# [| (); []; (::); true; false; Something|];;
- : t array = [|(); []; (::); true; false; Something|]

```

---

<div class="post-metadata">

**Author:** ![yawaramin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yawaramin/32/3384_2.png) [@yawaramin](https://discuss.ocaml.org/u/yawaramin)\
**Post date:** [September 10, 2021, 3:17pm UTC](https://discuss.ocaml.org/t/is-a-type-or-not/8464/3 "2021-09-10T15:17:06Z")

</div>

`[]` is a _data constructor,_ not a _type._ Replace it with `Nil` (just my choice of name, nothing special about it) everywhere in your code sample and you get exactly the same results.

---

<div class="post-metadata">

**Author:** ![viritrilbia](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/viritrilbia/32/3083_2.png) [@viritrilbia](https://discuss.ocaml.org/u/viritrilbia)\
**Post date:** [September 10, 2021, 3:50pm UTC](https://discuss.ocaml.org/t/is-a-type-or-not/8464/4 "2021-09-10T15:50:21Z")

</div>

Oh! I get it. So when I wrote

```auto
type foo = []

```

I was defining a new variant type `foo` with one constructor `[]` having no arguments. I have to say I think it’s super confusing to have just a few special cases of tokens that are interpreted as data constructors. At least in the case of

```auto
type foo = ()

```

I get a

```auto
Warning 65 [redefining-unit]: This type declaration is defining a new '()' constructor
which shadows the existing one.
Hint: Did you mean 'type foo = unit'?

```

Wouldn’t it make sense to give similar warnings for the other special cases?

---

<div class="post-metadata">

**Author:** ![yawaramin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yawaramin/32/3384_2.png) [@yawaramin](https://discuss.ocaml.org/u/yawaramin)\
**Post date:** [September 10, 2021, 3:53pm UTC](https://discuss.ocaml.org/t/is-a-type-or-not/8464/5 "2021-09-10T15:53:39Z")

</div>

Yeah, exactly. And you’re totally right, redefining `[]` should issue warning 65 too. Looks like a gap. Maybe file an issue on the compiler repo?

---

<div class="post-metadata">

**Author:** ![CraigFe](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/craigfe/32/1989_2.png) [@CraigFe](https://discuss.ocaml.org/u/CraigFe)\
**Post date:** [September 10, 2021, 3:54pm UTC](https://discuss.ocaml.org/t/is-a-type-or-not/8464/6 "2021-09-10T15:54:45Z")

</div>

Re-using the list data constructor names is occasionally useful, in particular when working with heterogeneous lists:

```ocaml
# type _ t =
  | [] : unit t
  | ( :: ) : 'a * 'b t -> ('a -> 'b) t ;;

# [1; "foo"; true] ;;
- : (int -> string -> bool -> unit) t

```

---

<div class="post-metadata">

**Author:** ![viritrilbia](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/viritrilbia/32/3083_2.png) [@viritrilbia](https://discuss.ocaml.org/u/viritrilbia)\
**Post date:** [September 10, 2021, 5:09pm UTC](https://discuss.ocaml.org/t/is-a-type-or-not/8464/7 "2021-09-10T17:09:30Z")

</div>

That’s neat; so even if you redefine `[]` and `(::)`, the builtin syntax `[_ ; _ ; ... ; _]` still expands in terms of the new ones. Why do you use `'a -> 'b` rather than, say, `'a * 'b`?

---

<div class="post-metadata">

**Author:** ![CraigFe](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/craigfe/32/1989_2.png) [@CraigFe](https://discuss.ocaml.org/u/CraigFe)\
**Post date:** [September 10, 2021, 5:12pm UTC](https://discuss.ocaml.org/t/is-a-type-or-not/8464/8 "2021-09-10T17:12:59Z")

</div>

`'a * 'b` also works, and is functionally equivalent 🙂

Using `->` has the minor convenience of not generating lots of nested parentheses in printed types, since `->` is already right-associative.

```ocaml
# type _ t =
  | [] : unit t
  | ( :: ) : 'a * 'b t -> ('a * 'b) t ;;

# [1; 2; 3; 4; 5] ;;
- : (int * (int * (int * (int * (int * unit))))) t

```

---

<div class="post-metadata">

**Author:** ![viritrilbia](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/viritrilbia/32/3083_2.png) [@viritrilbia](https://discuss.ocaml.org/u/viritrilbia)\
**Post date:** [September 10, 2021, 5:14pm UTC](https://discuss.ocaml.org/t/is-a-type-or-not/8464/9 "2021-09-10T17:14:06Z")

</div>

@CraigFe, do you disagree that redefining `[]` and `(::)` should issue a warning, because of that use case? Or would it be reasonable to issue a warning to help people like me, and let someone who really wants to use them for heterogeneous lists ignore or silence the warning?

How about redefining `()`, `true`, and `false` – are there any use cases for those?

---

<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:** [September 10, 2021, 5:20pm UTC](https://discuss.ocaml.org/t/is-a-type-or-not/8464/10 "2021-09-10T17:20:36Z")

</div>

> [@viritrilbia](#):
>
> Or would it be reasonable to issue a warning to help people like me, and let someone who really wants to use them for heterogeneous lists ignore or silence the warning?

The reason for the warning about `()` is because it is a common mistake and not very useful to redefine it. On the other hand, redefining _other_ predefined constructors is less common _and_ also more obviously useful. So it would seem OK _not_ to warn about redefining them.

Cheers,  
Nicolas

---

<div class="post-metadata">

**Author:** ![viritrilbia](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/viritrilbia/32/3083_2.png) [@viritrilbia](https://discuss.ocaml.org/u/viritrilbia)\
**Post date:** [September 10, 2021, 5:22pm UTC](https://discuss.ocaml.org/t/is-a-type-or-not/8464/11 "2021-09-10T17:22:16Z")

</div>

Is redefining `true` and `false` useful?

---

<div class="post-metadata">

**Author:** ![yawaramin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yawaramin/32/3384_2.png) [@yawaramin](https://discuss.ocaml.org/u/yawaramin)\
**Post date:** [September 10, 2021, 5:27pm UTC](https://discuss.ocaml.org/t/is-a-type-or-not/8464/12 "2021-09-10T17:27:53Z")

</div>

IMHO, it’s more a footgun than useful.

```auto
utop # type bool = true | false;;
utop # let x = true;;
utop # if x then 1 else 2;;
Error: This expression has type bool/1 but an expression was expected of type
         bool/2
       because it is in the condition of an if-statement
       File "_none_", line 1:
         Definition of type bool/2
       Hint: The type bool has been defined multiple times in this toplevel
         session. Some toplevel values still refer to old versions of this
         type. Did you try to redefine them?

```

---

<div class="post-metadata">

**Author:** ![viritrilbia](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/viritrilbia/32/3083_2.png) [@viritrilbia](https://discuss.ocaml.org/u/viritrilbia)\
**Post date:** [September 10, 2021, 5:30pm UTC](https://discuss.ocaml.org/t/is-a-type-or-not/8464/13 "2021-09-10T17:30:23Z")

</div>

How about a warning if `[]` is redefined without simultaneously redefining `(::)`? That seems more likely to indicate someone, like me, not knowing what they’re doing. I expect most if not all real use cases would involve redefining both together?

---

<div class="post-metadata">

**Author:** ![CraigFe](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/craigfe/32/1989_2.png) [@CraigFe](https://discuss.ocaml.org/u/CraigFe)\
**Post date:** [September 10, 2021, 5:38pm UTC](https://discuss.ocaml.org/t/is-a-type-or-not/8464/14 "2021-09-10T17:38:54Z")

</div>

> [@viritrilbia](#):
>
> @CraigFe, do you disagree that redefining `[]` and `(::)` should issue a warning, because of that use case? Or would it be reasonable to issue a warning to help people like me, and let someone who really wants to use them for heterogeneous lists ignore or silence the warning?

I was curious about how prevalent redefining `(::)` is in the `opam` repository, and it seems to happen quite a bit:

> **List of packages**
>
> ```auto
> $ opam grep "( *:: *)"
> 
> ```
> 
> - `accessor`
> - `amqp-client`
> - `amqp-client-async`
> - `amqp-client-lwt`
> - `archi`
> - `archi-async`
> - `archi-lwt`
> - `b0`
> - `batsat`
> - `batteries`
> - `bitwuzla`
> - `bitwuzla-bin`
> - `bitwuzla-c`
> - `blake3`
> - `brr`
> - `bsbnative`
> - `camlon`
> - `camlp5`
> - `coccinelle`
> - `codept`
> - `colombe`
> - `conformist`
> - `containers`
> - `containers-data`
> - `containers-thread`
> - `coq`
> - `coq-of-ocaml`
> - `coqide`
> - `crowbar`
> - `doc-ock`
> - `dot-merlin-reader`
> - `earley`
> - `elpi`
> - `embedded_ocaml_templates`
> - `extlib`
> - `farfadet`
> - `GT`
> - `git-cohttp-mirage`
> - `gopcaml-mode-merlin`
> - `graphql`
> - `graphql-async`
> - `graphql-cohttp`
> - `graphql-lwt`
> - `graphql_parser`
> - `immutable`
> - `iocaml`
> - `jsonrpc`
> - `ketrew`
> - `learn-ocaml`
> - `learn-ocaml-client`
> - `lem`
> - `linksem`
> - `liquidsoap`  
> …  
> (At this point, `opam grep` ran out of disk space on my machine… 😁.)

My suspicion is that adding a naive warning for redefinition of `(::)` would overall cause more inconvenience than it would save, although it might make sense to warn on redefinition of `(::)` but not `[]` in the same type. (This still warns against some valid use-cases, but I would guess _far_ fewer of these occur in the wild.)

I agree that the construct `type t = []` in particular is very surprising with regard to polymorphic variants. I would propose adding a subsection to the OCaml manual on polymorphic variants explaining:

- the (lack of a) constructor-less polymorphic variant type, and the reasoning for this;
- the gotcha mentioned here w.r.t. `[]`;
- a pointer to the section on empty variant types.

Ultimately, I think just making `type foo = []` invalid wouldn’t be enough to fix the gotcha: there needs to be a longer-form explanation somewhere (although this could be linked from a hint, as is done for some of the other warnings).

Relatedly, there was [some discussion back in 2013](https://github.com/ocaml/ocaml/issues/5936) about banning redefinition of `(::)` altogether, but this doesn’t seem to have led anywhere.

---

<div class="post-metadata">

**Author:** ![SkySkimmer](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/skyskimmer/32/1672_2.png) [@SkySkimmer](https://discuss.ocaml.org/u/SkySkimmer)\
**Post date:** [September 10, 2021, 6:19pm UTC](https://discuss.ocaml.org/t/is-a-type-or-not/8464/15 "2021-09-10T18:19:45Z")

</div>

Those are not all redefinitions, for instance the `(::)` in batsat are in rust code.

---

<div class="post-metadata">

**Author:** ![viritrilbia](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/viritrilbia/32/3083_2.png) [@viritrilbia](https://discuss.ocaml.org/u/viritrilbia)\
**Post date:** [September 10, 2021, 8:08pm UTC](https://discuss.ocaml.org/t/is-a-type-or-not/8464/16 "2021-09-10T20:08:38Z")

</div>

Speaking of footguns, it appears that if you redefine `()` to take an argument, it completely fubars the parser or something:

```ocaml
# type foo = () of int;;
Warning 65 [redefining-unit]: This type declaration is defining a new '()' constructor
which shadows the existing one.
Hint: Did you mean 'type foo = unit'?
type foo = () of int

# 2 * (1 + 1);;
Error: The constructor () expects 1 argument(s),
       but is applied here to 0 argument(s)

# let f x = x;;
Error: The constructor () expects 1 argument(s),
       but is applied here to 0 argument(s)

```

---

<div class="post-metadata">

**Author:** ![yawaramin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yawaramin/32/3384_2.png) [@yawaramin](https://discuss.ocaml.org/u/yawaramin)\
**Post date:** [September 10, 2021, 8:32pm UTC](https://discuss.ocaml.org/t/is-a-type-or-not/8464/17 "2021-09-10T20:32:05Z")

</div>

Nice find! I wonder how long this has been lurking in the shadows 😁

---

<div class="post-metadata">

**Author:** ![octachron](https://avatars.discourse-cdn.com/v4/letter/o/49beb7/32.png) [@octachron](https://discuss.ocaml.org/u/octachron)\
**Post date:** [September 10, 2021, 9:54pm UTC](https://discuss.ocaml.org/t/is-a-type-or-not/8464/18 "2021-09-10T21:54:04Z")

</div>

Redefining `()` as

```ocaml
type t = () of int

```

does not mess up with the parser. (and you can even construct value of this type with

```ocaml
let x= () 0

```

The responsible is utop which is rewritting

```ocaml
1 + 2;;

```

as

```ocaml
let () = 1 + 2

```

which does not work when `()` is not the expected unit.

---

<div class="post-metadata">

**Author:** ![viritrilbia](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/viritrilbia/32/3083_2.png) [@viritrilbia](https://discuss.ocaml.org/u/viritrilbia)\
**Post date:** [September 10, 2021, 11:44pm UTC](https://discuss.ocaml.org/t/is-a-type-or-not/8464/19 "2021-09-10T23:44:16Z")

</div>

I see that it works fine in a source file rather than at toplevel. But `let () = 1 + 2` doesn’t typecheck even ordinarily – `1 + 2` is not of type `unit` – so that can’t be exactly what utop is doing, can it?

---

<div class="post-metadata">

**Author:** ![EduardoRFS](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/eduardorfs/32/2259_2.png) [@EduardoRFS](https://discuss.ocaml.org/u/EduardoRFS)\
**Post date:** [September 11, 2021, 12:02am UTC](https://discuss.ocaml.org/t/is-a-type-or-not/8464/20 "2021-09-11T00:02:52Z")

</div>

Because that’s the wrong syntax for pattern matching variants, essentially you’re doing, notice that all `let` is a pattern matching.

```ocaml
type t = Variant of int
let Variant = 1 + 2

```

[Next page](https://discuss.ocaml.org/t/is-a-type-or-not/8464.md?page=2)
