Jenis Runtime: Deeper Down the Rabbit Hole

Ketika saya mulai menulis catatan " Jenis yang tidak diharapkan ", bagi saya tampaknya saya telah menguasai cara membawa jenis Erlang ke runtime dan sekarang saya dapat menggunakannya dalam kode klien di elixir. Haha, betapa naifnya aku.



Apa pun yang disarankan oleh tautan akan berfungsi untuk definisi jenis tempat penggunaan yang eksplisit seperti use Foo, var: type(). Sayangnya, pendekatan ini gagal jika kita ingin mendefinisikan tipe di tempat lain: di sebelahnya dalam kode menggunakan atribut modul, atau, di sana, di konfigurasi. Misalnya, untuk mendefinisikan sebuah struktur, kita mungkin ingin menulis sesuatu seperti ini:



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

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


Mercusuar di Katalonia Prancis



Kode di atas bukan berarti ia tidak akan menangani jenis seperti yang kita inginkan - ia tidak akan mengumpulkan sama sekali karena akan @definition var: atom()memunculkan pengecualian ** (CompileError) undefined function atom/0.



Pendekatan naif



 โ€” ยซ ยป ( @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


Saya tidak tahu apa nilai praktis dari pustaka ini dalam produksi (bohong, saya tahu: tidak ada), tetapi ini pasti bisa sangat membantu selama pengembangan, memungkinkan Anda mempersempit pencarian dan mengisolasi kesalahan aneh terkait dengan sifat dinamis jenis di Elixir terutama saat berhadapan dengan sumber eksternal.



Semua kode perpustakaan tersedia, seperti biasa, di github .






Selamat mengetik runtime!




All Articles