Do these type make any sense?

 @callback userRandom() :: {:ok, String.t} | {:error, nil}

like I return a random username, its either successful ok and returns a string and if it errors then it doesnt return anything nil

I wouldn‘t return nil but rather an error description in the form of an atom like :insufficient_entropy.


There’s nothing wrong with what you’ve got, per se, but you could simplify it. Note that there’s no advantage for the return values to be the same type (tuple in your example). {:error, nil} doesn’t tell you anything that simply returning :error by itself doesn’t tell you.

As is pointed out, it’s common for the error to be a tuple, but something indicative of the reason for the error. For example, {:error, :not_found} would be more informative to a caller than just :error or {:error, nil}. The {:error, <reason>} pattern sets you up for the future better for handling issues if the code might error for different reasons.

Actually, you’ll see a similar pattern quite often (just the reverse of what you’re doing):

@spec some_func() :: :ok | {:error, reason :: term()}

Naturally, you’re returning a value in the OK case, but the example above demonstrates the same kind of simplification I’m referring to.

1 Like

Looks great to me! I dig it :slight_smile:

1 Like

Yea so instead of the nil I can try to describe the issue like :not_found?


Consider this example from Ecto (Ecto.Repo — Ecto v3.8.4):

@callback start_link(opts :: Keyword.t()) ::
  {:ok, pid()} | {:error, {:already_started, pid()}} | {:error, term()}

This goes a step further defining two possible error returns: one case where well defined additional info is available and another more generic error case.

1 Like

Yes. You can then match on that return value in other functions where you would want to e.g. put a proper error log message somewhere.

1 Like