Stream: beginners

Topic: Why do wildcard types exist ?


view this post on Zulip Paul Le Corre (Nov 08 2023 at 14:51):

Hi, just a bit of feedback on the wildcard types part of the tutorial (https://www.roc-lang.org/tutorial#wildcard-type).

Type variables have been easy to understand because I just mapped them to Rust's generics. I've been confused of why a wildcard was needed in isEmpty : List * -> Bool because I'm naturally excepting isEmpty : List a -> Bool to work just fine. Which makes me wonder why the wildcard type exist.

view this post on Zulip Anton (Nov 08 2023 at 15:07):

The type of List.isEmpty actually is List a -> Bool. I also checked the roc codebase, examples and basic-cli and we never use List * in any roc file. This also makes me wonder why we have wildcard types :p

view this post on Zulip Anton (Nov 08 2023 at 15:10):

Maybe it's for type inference, when you use an empty list somewhere without providing a type annotation.

view this post on Zulip Brian Carroll (Nov 08 2023 at 15:24):

If there's only one type variable in your signature then it makes no difference whether you use * or a
But consider these signatures

  1. List a, List a -> Bool
  2. List a, List b -> Bool
  3. List *, List * -> Bool

1 and 2 are different signatures. In signature 1, the two lists must contain the same type of value. In signature 2 that is not a constraint. They can be different or the same.
Signatures 2 and 3 are the same.
Signature 3 is just a convenience notation for signature 2.

view this post on Zulip Paul Le Corre (Nov 08 2023 at 15:42):

oh alright, got it now, thank you very much !

view this post on Zulip Paul Le Corre (Nov 08 2023 at 15:44):

Maybe it's a concept that could be approached later in the tutorial ? At least, after the variable types part, as it seems mainly used for type inference and not much in day to day development.

view this post on Zulip Paul Le Corre (Nov 08 2023 at 15:46):

Let me know if you need help updating that part.

view this post on Zulip Anton (Nov 08 2023 at 16:02):

I wonder if we should cut the wildcard types section :thinking:

view this post on Zulip Anton (Nov 08 2023 at 16:04):

perhaps it does come up often enough in the repl or in error messages...

view this post on Zulip Anton (Nov 08 2023 at 16:08):

We could also move it to the examples repo

view this post on Zulip Brian Carroll (Nov 08 2023 at 16:17):

I think the main place it comes up is the standard library documentation.

view this post on Zulip Brendan Hansknecht (Nov 08 2023 at 16:17):

Probably worth a mention in a subsection at the end of the type variable section. Just something short, maybe even brians example

view this post on Zulip Anton (Nov 08 2023 at 16:20):

I think the main place it comes up is the standard library documentation.

The standard library documentation is all in roc files right? And I searched for List * in all roc files in the repo and got nothing, so seems like it's not in the documentation either.

view this post on Zulip Brian Carroll (Nov 08 2023 at 16:20):

I think since it's in the std lib it should be mentioned in the tutorial. It's true though that in application code you don't often write *, it's mostly for library code. So a beginner intro should cover how to read it at least.

view this post on Zulip Brian Carroll (Nov 08 2023 at 16:20):

Oh!

view this post on Zulip Anton (Nov 08 2023 at 16:30):

I was still focused on List *, it does come up regularly for Dict, Set, and when using Task.

view this post on Zulip Anton (Nov 08 2023 at 18:53):

I've changed List isEmpty, withCapacity and len to use a wildcard for consistency with Dict and Set.

view this post on Zulip Paul Le Corre (Nov 08 2023 at 19:12):

Is it something that could be removed from the language ? Inferred signatures could have default variable type names such as a, b, c, .., aa. As a beginner not used to FP notations, it seems clearer to not have both. But I may be missing important uses cases.

view this post on Zulip Brendan Hansknecht (Nov 08 2023 at 19:27):

It could be replaced, but seeing List a is meant to have a meaning. a is a type variable that is expected to be matched with other locations. By writing a it is kinda like specifying that you care about the type.

(List a, a) means that I care about the element type of the list. It must match the other value in the tuple.

Seeing (List a, b, List c, d) is just noise. None of those type variable have any meaning. As a reader of the type signiture, I expect to see the type variables used elsewhere, but they aren't.

Seeing a * is a clear signal that the type can not matter. There is no way it can be depended on or matched at all.

view this post on Zulip Paul Le Corre (Nov 08 2023 at 20:14):

Indeed, the nuance was unclear to me, but makes perfect sense now. Thanks for sharing !


Last updated: Jul 06 2025 at 12:14 UTC