Sgoettschkes

Sgoettschkes

Typespec and opts

Hi,

I’m trying to find examples of typespec usage with optional arguments. I assume the following is one way to write a spec with optional arguments:

@spec example1(String.t(), option1: String.t(), option2: integer()) :: String.t()
def example1(str, opts \\ [])

If I’d like to extract the options into a type because it is used in other places as well, would this be the correct way:

@type opts :: [option1: String.t(), option2: integer()]
@spec example1(String.t(), opts | nil) :: String.t()
def example1(str, opts \\ [])

Do I even need the | nil part?

Thanks,
Sebastian

First Post!

NobbZ

NobbZ

Both of your approaches require the elements of the option list to appear in the correct order, and also that all of them appear.

You probably want this:

@type opts :: {:option1, String.t()} | {:option2, integer()}
@spec example(String.t(), [opts]) :: String.t()

Most Liked

LostKobrakai

LostKobrakai

Typespecs don’t have optional arguments. If you want to supply a typespec for both generated arities you need two specs:

@spec example1(String.t()) :: String.t()
@spec example1(String.t(), opts) :: String.t()
def example1(str, opts \\ []), do: …

Optional arguments do not mean they become nil when absent. Your function will become the following:

def example1(str), do: example1(str, [])
def example1(str, opts), do: …

There’s no implicit data being injected anywhere.

ityonemo

ityonemo

I am pretty sure that is not the case. There is no list type in the dialyzer typesystem that remembers the order of the internal types (or any requirement for an internal type to exist besides the general “nonempty”), and Elixir has special cased keyword lists in typespecs (Typespecs reference — Elixir v1.20.2, section “keyword list”), even though it looks a bit weird because no other list types have commas in them. Also it’s a bit strange that the documentation doesn’t mention that you can use more than one k/v types in the kwl special syntax, but you can… Maybe I should PR a fix to that doc.

Both of @Sgoettschkes examples are syntactically correct. 1 and 2 without the nil are semantically correct for what he’s trying to do. I would generally disfavor spec #1 because one doesn’t expect to unroll Elixir’s synatactic sugar inside of a typespec. It looks confusing and could be mistaken for an arity-3 function. #2 (without the nil) is very much idiomatic elixir; nobody really types out the version of the function with the default value, and I think that the dialyzer system is smart enough to deal with it correctly if you omit the lower-arity functions.

@NobbZ’s example is closest to what you would idiomatically see in erlang typespecs, and it’s the most flexible, since you can later do type magic and pull apart groups of options, for the purposes of documenting them separately, or applying them in separate cases if some functions take only a subset of the options.

NobbZ

NobbZ

You are indeed correct, my “order” argument isn’t valid. I got confused with pattern matching on keyword lists.

Last Post!

Sgoettschkes

Sgoettschkes

Thanks for your comments. I decided to use the spec suggested by LostKobrakai:

@spec example1(String.t()) :: String.t()
@spec example1(String.t(), opts) :: String.t()
def example1(str, opts \\ []), do: …

I do think this makes it very clear how to call the function.

Where Next?

Popular in Questions Top

vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New

Other popular topics Top

electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" => #BSON.ObjectId<58eb1a7a9ad169198c3dXXXX>, "email" => ...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New

We're in Beta

About us Mission Statement