Sgoettschkes

Sgoettschkes

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

Showing Posts 1 to 7

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()
Sgoettschkes

Sgoettschkes OP

Are you sure that all elements are required in that order in my first example? At least dialyzer is not picking this up with a straight up call to example1 with only a string passed. From what I understand this should be picked up?

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.

ityonemo

ityonemo

I think that’s also a good argument as to why you shouldn’t use form #1 even if it is legal and semantically correct!

Sgoettschkes

Sgoettschkes OP

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.

— All posts loaded —

Where Next? Top

Trending in Questions Top

Blokh
Hey guys, I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly Do you guys have any suggestions what is the best prac...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
Onor.io
I have what I’ve heard referred to as a “lookup table” in my database. This is a way of assigning codes to common values. One common lo...
New
jaybe78
Hello, I’m developing a online persistent chat system (what’s app) like using elixir/dynamodb/aws for a mobile app(flutter). The diffic...
New
Trolleger
What approach to take when sending live updates to “random” users Hi! I have a question, I have a little chat app, and when I create a DM...
New
matt-savvy
Anyone here using Honeybadger? My Honeybadger account is being overwhelmed with noise from some bots. Seeing a lot of Bandit.HTTPError...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New

Other Trending Topics Top

garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
wintermeyer
There are three potential reasons for members of this forum to have a look at https://vutuv.de You are tired or annoyed of LinkedIn. Yo...
New
webofbits
Aludel - LLM Evaluation Workbench Aludel is an embeddable Phoenix LiveView dashboard for evaluating and comparing LLM prompts across mult...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews