Types d'exécution: plus profondément dans le terrier du lapin

Quand j'ai commencé à écrire une note " Types où ils n'étaient pas attendus ", il m'a semblé que j'avais maîtrisé pour amener les types Erlang au runtime et maintenant je peux les utiliser dans le code client sur l'élixir. Haha, comme j'étais naïf.



Tout ce qui est suggéré par le lien fonctionnera pour les définitions de type de lieu d'utilisation explicites telles que use Foo, var: type(). Malheureusement, cette approche est vouée à l'échec si nous voulons définir des types ailleurs: à côté dans le code en utilisant les attributs du module, ou, là, dans la configuration. Par exemple, pour définir une structure, nous pourrions vouloir écrire quelque chose comme ceci:



# @fields [foo: 42]
# defstruct @fields

@definition var: atom()
use Foo, @definition


Phare en Catalogne française



Le code ci-dessus n'est pas qu'il ne gérera pas le type comme nous le souhaitons - il ne collectera pas du tout car il @definition var: atom()lèvera une exception ** (CompileError) undefined function atom/0.



Approche naĂŻve



 â€” « » ( @tsilb, , .) , , , , â€” .



, , __using__/1: , ( field → type()),  â€” , , , {Module, :type, [params]}. ~q||, , , , AST. quote/1 : foo: ~q|atom()|. , , . . , - , , , , . , , - - - , .



. , erlang  â€”  , , .  â€” , , ( ).



, . , , , , , , â€”  . , .



Tyyppi



,  â€” , XY . , , —   â€” . Tyyppi.



Code.Typespec, . : . , , , . , , .  â€”  Tyyppi.of?/2, ,  â€” «»/«» , .



iex|tyyppi|1  Tyyppi.of? GenServer.on_start(), {:ok, self()}
#⇒ true
iex|tyyppi|2  Tyyppi.of? GenServer.on_start(), :ok
#⇒ false


- , Tyyppi.T. Tyyppi.of?/2 - â€” Tyyppi.of_type?/2.



iex|tyyppi|3  type = Tyyppi.parse(GenServer.on_start)
iex|tyyppi|4  Tyyppi.of_type? type, {:ok, self()}
#⇒ true


, , , , , . :erlang.term_to_binary/1, Config.Provider.





, : . , . , key: type(). Access, upserts. , Ecto.Changeset cast_field/1 validate/1.



, , , , ( , ).



defmodule MyStruct do
  import Kernel, except: [defstruct: 1]
  import Tyyppi.Struct, only: [defstruct: 1]

  @typedoc "The user type defined before `defstruct/1` declaration"
  @type my_type :: :ok | {:error, term()}

  @defaults foo: :default,
            bar: :erlang.list_to_pid('<0.0.0>'),
            baz: {:error, :reason}
  defstruct foo: atom(), bar: GenServer.on_start(), baz: my_type()

  def cast_foo(atom) when is_atom(atom), do: atom
  def cast_foo(binary) when is_binary(binary),
    do: String.to_atom(binary)

  def validate(%{foo: :default} = my_struct), do: {:ok, my_struct}
  def validate(%{foo: foo} = my_struct), do: {:error, {:foo, foo}
end


Je n'ai aucune idée de la valeur pratique de cette bibliothèque en production (mensonge, je sais: aucune), mais cela peut certainement être une aide précieuse pendant le développement, vous permettant d'affiner votre recherche et d'isoler d'étranges erreurs associées à la nature dynamique des types dans Elixir surtout lorsqu'il s'agit de sources externes.



Tout le code de la bibliothèque est disponible, comme toujours, sur github .






Bonne saisie à l'exécution!




All Articles