I think parameterized types might help a little in this case: @type t() :: t(integer() | nil)
@type t(a) :: %__MODULE__{ a: a, b: integer() | nil, c: integer() | nil } @type my_struct_with_a() :: t(integer) On Saturday, February 6, 2021 at 7:50:41 AM UTC+1 José Valim wrote: > I completely agree with the needs for this feature but unfortunately it > must be implemented upstream on Erlang first. > > The reason why is because if we were to expand the initial map within > Elixir, it will both be expensive (we may need to augment a remote type, > which would require loading and traversing beam files) and add a compile > time dependency. > > If this is part of Erlang, none of those are required. > > On Sat, Feb 6, 2021 at 01:54 Bernardo Amorim <bamo...@gmail.com> wrote: > >> I was expecting that to be a compile time transformation that would >> generate the exact same compiled erlang as >> ``` >> @type my_struct_with_a() :: %MyStruct{ >> a: integer(), >> b: integer() | nil, >> c: integer() | nil >> } >> ``` >> >> But that would require `@type` to be able to get the AST for >> `MyStuct.t()` during compile time. >> >> On Fri, Feb 5, 2021 at 8:21 PM Louis Pilfold <lo...@lpil.uk> wrote: >> >>> Hello! >>> >>> Correct me if I'm wrong but I believe this isn't something that Erlang >>> typespecs support, or at least there is no syntax for it. >>> >>> What do you see this compiling to? Elixir has to work with the >>> capability of Erlang here. >>> >>> Cheers, >>> Louis >>> >>> On Fri, 5 Feb 2021, 22:39 Bernardo Amorim, <bamo...@gmail.com> wrote: >>> >>>> Hi folks, I'm not even sure if this is possible nor if it was raised >>>> before nor if there is already another way of doing something similar. But >>>> this is something that I've been thinking a lot recently and I'd like to >>>> know if it is possible and desirable. If it is, I can help with a PR later >>>> on. >>>> >>>> The "problem": >>>> >>>> It is usual to when we have a Struct (specially Ecto.Schemas) to define >>>> a `@type t()` with the fields. Let's say we have a Struct with a few >>>> fields >>>> like this: >>>> >>>> ``` >>>> defmodule MyStruct do >>>> defstruct [:a, :b, :c] >>>> @type t() :: %__MODULE__{ >>>> a: integer() | nil, >>>> b: integer() | nil, >>>> c: integer() | nil >>>> } >>>> ``` >>>> >>>> Now imagine I want to define a function that receives a struct but >>>> requires one of these fields to be non null. What we have to do right now >>>> is to: >>>> >>>> ``` >>>> @type my_struct_with_a() :: %MyStruct{ >>>> a: integer(), >>>> b: integer() | nil, >>>> c: integer() | nil >>>> } >>>> ``` >>>> >>>> (Ok, maybe in some cases we do not need to redefine all the other >>>> fields since they might be irrelevant, but let's assume we actually want >>>> to >>>> type everything) >>>> >>>> The proposal: >>>> >>>> For map values when we just want to change one field we can do >>>> something like: ```%MyStruct{my_struct | a: 1}```, but for types this is >>>> not possible. >>>> >>>> My proposal would be to have something like this: >>>> >>>> ``` >>>> @type my_struct_with_a() :: %MyStruct{MyStruct.t() | a: integer()} >>>> ``` >>>> >>>> That would generate a type with all fields copied from `MyStruct.t()` >>>> but with `:a` changed to `integer()` instead of `integer() | nil`. >>>> >>>> The caveats I see is what to do when the type is not just a map type >>>> (maybe it is a sum type, maybe it is not even a map, etc). >>>> >>>> What do you folks think about this? >>>> >>>> Thanks, >>>> Bernardo Amorim >>>> >>>> -- >>>> You received this message because you are subscribed to the Google >>>> Groups "elixir-lang-core" group. >>>> To unsubscribe from this group and stop receiving emails from it, send >>>> an email to elixir-lang-co...@googlegroups.com. >>>> To view this discussion on the web visit >>>> https://groups.google.com/d/msgid/elixir-lang-core/b152b04f-3c6b-4052-92aa-d23724ed0f92n%40googlegroups.com >>>> >>>> <https://groups.google.com/d/msgid/elixir-lang-core/b152b04f-3c6b-4052-92aa-d23724ed0f92n%40googlegroups.com?utm_medium=email&utm_source=footer> >>>> . >>>> >>> -- >>> >> You received this message because you are subscribed to a topic in the >>> Google Groups "elixir-lang-core" group. >>> To unsubscribe from this topic, visit >>> https://groups.google.com/d/topic/elixir-lang-core/gvLUM0asfmI/unsubscribe >>> . >>> To unsubscribe from this group and all its topics, send an email to >>> elixir-lang-co...@googlegroups.com. >>> To view this discussion on the web visit >>> https://groups.google.com/d/msgid/elixir-lang-core/CABu8xFCeqgNAOp1o%3D0JJ%2B9JYwF20H9-%2BnD2DLoY%3DuSisyGB8NA%40mail.gmail.com >>> >>> <https://groups.google.com/d/msgid/elixir-lang-core/CABu8xFCeqgNAOp1o%3D0JJ%2B9JYwF20H9-%2BnD2DLoY%3DuSisyGB8NA%40mail.gmail.com?utm_medium=email&utm_source=footer> >>> . >>> >> -- >> You received this message because you are subscribed to the Google Groups >> "elixir-lang-core" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to elixir-lang-co...@googlegroups.com. >> > To view this discussion on the web visit >> https://groups.google.com/d/msgid/elixir-lang-core/CAJVmgvBectadT%3DYaT7tFtyG03APUcsyJWQU8922Y_9zuYb7jxA%40mail.gmail.com >> >> <https://groups.google.com/d/msgid/elixir-lang-core/CAJVmgvBectadT%3DYaT7tFtyG03APUcsyJWQU8922Y_9zuYb7jxA%40mail.gmail.com?utm_medium=email&utm_source=footer> >> . >> > -- You received this message because you are subscribed to the Google Groups "elixir-lang-core" group. To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-core+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/4f7a133f-3394-4376-bf6c-e87bf0f8a1a7n%40googlegroups.com.