# Best way to ensure only certain values are allowed for a particular arg?

**URL:** https://discourse.julialang.org/t/best-way-to-ensure-only-certain-values-are-allowed-for-a-particular-arg/120952
**Category:** General Usage
**Tags:** enum
**Created:** [October 5, 2024, 7:06am UTC](https://discourse.julialang.org/t/best-way-to-ensure-only-certain-values-are-allowed-for-a-particular-arg/120952 "2024-10-05T07:06:21Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![xiaodai](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xiaodai/32/15937_2.png) [@xiaodai](https://discourse.julialang.org/u/xiaodai)
#### Post date: [October 5, 2024, 7:06am UTC](https://discourse.julialang.org/t/best-way-to-ensure-only-certain-values-are-allowed-for-a-particular-arg/120952/1 "2024-10-05T07:06:21Z")

</div>

In trying to write a clone of numpy API in Julia, I see the `numpy.array` has an argument which accepts only 4 values but is other a `string` in Python.

The 4 possibles values are `C` , `F`, `A`, `K`. So I want to make sure that arg only accepts these 4 values. So the first thing I tried is to make a struct with a construction

```julia
struct ARRAY_ORDER
    order::Char

    ARRAY_ORDER(order) = begin
        @assert order in ('C', 'F', 'A', 'K')
        new(order)
    end
end

```

then I thought, actually because the `order` value is typed Char technically someone can make a constructor and do type piracy and allow some other value to be input. So that’s not ideal.

Another way is to put an assert into the `array` function in Julia but I prefer to use types of restrict.

AI says to use `Enum` which is also problematic as it requires conversation and I want to store the order as `Char` as originally intended.

Then I focused on this solution which I think is safe from type-piracy.

```julia
struct ARRAY_ORDER
    order::Union{Val{'C'}, Val{'F'}, Val{'A'}, Val{'K'}}

    ARRAY_ORDER(order::Char) = begin
        new(Val(order))
    end

    ARRAY_ORDER(order::String) = begin
        @assert length(order) == 1 "order must be a single character"
        orderchar = String[1]
        new(Val(orderchar))
    end
end

```

which is using `Val` and setting order to be a few distinct values using the `Val` constructor to turn values into types.

Is this the best way? Are there alternatives?

---

<div class="post-metadata">

### Author: ![abraemer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/abraemer/32/51403_2.png) [@abraemer](https://discourse.julialang.org/u/abraemer)
#### Post date: [October 5, 2024, 7:36am UTC](https://discourse.julialang.org/t/best-way-to-ensure-only-certain-values-are-allowed-for-a-particular-arg/120952/2 "2024-10-05T07:36:15Z")

</div>

There is not a single best way I think. However you should realize that there are tradeoffs here! When you put more information in the type domain, the compiler will try to use it! This can dramatically change the performance of generated code.

E.g. this

> [@xiaodai](#):
>
> `order::Union{Val{'C'}, Val{'F'}, Val{'A'}, Val{'K'}}`

Means that the compiler does not know the type of the `order` field which could lead to runtime dispatches. These are a lot more costly than simple `if-else` checking the value. OTOH if the type is inferrible from the context then the check can be ellided which is faster of course.

So basically there are multiple routes:

1. Not using types to constrain the values. This is essentially the numpy way. I.e. you have a `Char` field and check it’s value. This is simple, easy and doesn’t come with compiler/performance implications
2. Using types to model the values but keep them as field. In this case I would advise against a `Union` because you need to rely on union-splitting for performance and instead use a SumType from LightSumTypes.jl. However conceptually this is not too different from checking the values with `if-else` and just has potential performance pitfalls on top imo.
3. You could make the the `ARRAY_ORDER` struct parametric itself instead of storing the order as field (and then constrain the possible values of the parameter in the constructor). This has the potential to be fastest as long as the type stays inferrible. It is also conceptually cleanest since now the information is completely in the type domain.

---

<div class="post-metadata">

### Author: ![xiaodai](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xiaodai/32/15937_2.png) [@xiaodai](https://discourse.julialang.org/u/xiaodai)
#### Post date: [October 5, 2024, 7:57am UTC](https://discourse.julialang.org/t/best-way-to-ensure-only-certain-values-are-allowed-for-a-particular-arg/120952/3 "2024-10-05T07:57:43Z")

</div>

> [@abraemer](#):
>
> You could make the the `ARRAY_ORDER` struct parametric itself

So something like ARRAY\_ORDER{‘C’}. But how do i restrict the possible values ?

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [October 5, 2024, 9:36am UTC](https://discourse.julialang.org/t/best-way-to-ensure-only-certain-values-are-allowed-for-a-particular-arg/120952/4 "2024-10-05T09:36:45Z")

</div>

> [@xiaodai](#):
>
> technically someone can make a constructor and do type piracy and allow some other value to be input

The only way `ARRAY_ORDER` is protected from a user expanding the orders is that the fields in structs are currently not redefineable, which may change one day. Assuming the user strangely knows how to implement new useful behaviors for functions like `numpy.array`, nothing stops them from reading your source code and defining methods to allow more orders, maybe with a variant of `ARRAY_ORDER` or without it at all. Tradeoff of Julia being interactive is you can’t protect users from their creativity. Much of NumPy is safe from tampering (and reflection) because a lot of it is in C; maybe juliac can pull something like this off.

Assuming they’re not willing to tamper with API functions, then it’s fine to do the necessary branch over the different orders and throw the “order not understood” error otherwise.

---

<div class="post-metadata">

### Author: ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)
#### Post date: [October 5, 2024, 10:17am UTC](https://discourse.julialang.org/t/best-way-to-ensure-only-certain-values-are-allowed-for-a-particular-arg/120952/5 "2024-10-05T10:17:53Z")

</div>

> [@xiaodai](#):
>
> AI says to use `Enum` which is also problematic as it requires conversation and I want to store the order as `Char` as originally intended.

Enums (not necessarily `@enum`, there are also some relevant packages) are what you want.
