You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/concepts.rst
+126-2Lines changed: 126 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -927,8 +927,132 @@ Suppose you wanted to serialize :external:py:class:`datetime.datetime` instances
927
927
Data Types and Records
928
928
----------------------
929
929
930
-
TBD
930
+
Basilisp allows 3 different methods for defining custom data types which implement Python interfaces and :ref:`protocols`, detailed in the sections below.
931
+
932
+
Each of the methods Basilisp supports for creating custom data types may implement 0 or more Python interfaces and Basilisp protocols.
933
+
Types are required to implement every function defined by any declared interfaces and protocols.
934
+
935
+
Types may also optionally implement 0 or more Python `"dunder" methods <https://docs.python.org/3/reference/datamodel.html>`_ without implementing every such method.
936
+
937
+
.. note::
938
+
939
+
It is not necessary to declare :external:py:class:`object` as a superclass, but doing so is not an error.
940
+
941
+
.. warning::
942
+
943
+
Python, unlike Java, does not have a true "interface" type.
944
+
The best approximation is :external:py:class:`abc.ABC`, although this type is merely advisory and many libraries and applications eschew its use.
945
+
946
+
For the cases where a host type is not defined as an ``abc.ABC`` instance, users can override the compiler check by setting the ``^:abstract`` meta key on the interface type symbol passed to the ``deftype`` form.
947
+
For example, take :external:py:class:`argparse.Action` which is required to be implemented for customizing :external:py:mod:`argparse` actions, but which is not defined as an ``abc.ABC``:
948
+
949
+
.. code-block::
950
+
951
+
(import argparse)
952
+
953
+
(reify
954
+
^:abstract argparse/Action
955
+
(__call__ [this]
956
+
;; ...
957
+
))
958
+
959
+
.. _deftype:
960
+
961
+
``deftype``
962
+
^^^^^^^^^^^
963
+
964
+
In many cases it is desirable or necessary to define a Python class (or object instance which is a subtype of some type) to interact with a Python library.
965
+
To facilitate this, Basilisp includes the :lpy:fn:`deftype` macro for creating Python classes which optionally implement Python interfaces or Basilisp protocols.
966
+
967
+
Types defined via ``deftype`` may include 0 or more fields which are required at object instantiation.
968
+
Fields defined in ``deftype`` forms are immutable by default.
969
+
Attempting to set a field using :lpy:form:`set!` will result in a compile-time error.
970
+
However, it is possible to mark a field as mutable by using the ``^:mutable`` metadata on a ``deftype`` field at compile time.
971
+
Mutable fields may be ``set!`` from within class methods.
972
+
Fields may be referred to freely by name from within method definitions as in Java (and unlike in Python where they must be qualified with ``self``).
973
+
Fields may also specify defaults by providing the default value as a ``^:default`` metadata value.
974
+
975
+
.. warning::
976
+
977
+
Python is known for taking a rather lax stance on object mutability as compared to many other languages and runtimes.
978
+
As a consequence of the language and VM not enforcing true immutability, even immutable fields may still be modified by other means.
979
+
Users should not take the immutable default state of ``deftype`` fields as a guarantee, but rather as a principled approach to reducing the surface area of potential bugs due to mutability.
980
+
981
+
Types created by ``deftype`` automatically have some basic sensible defaults added via `attrs <https://www.attrs.org/en/stable/>`_, such as a constructor (whose argument order matches that of the defined fields) and Python ``__str__`` and ``__repr__`` methods.
982
+
User supplied versions of methods besides ``__init__`` may override the generated variants in all cases.
983
+
984
+
Methods may be defined with multiple arities if required by any declared protocols.
985
+
``deftype`` methods may be :ref:`defined with support for Python kwargs <basilisp_functions_with_kwargs>` exactly like plain functions.
986
+
Methods may be declared as by :external:py:func:`classmethod` and :external:py:func:`staticmethod` using the ``^:classmethod`` and ``^:staticmethod`` metadata respectively on the method name.
987
+
Static and classmethods may be defined with multiple arities.
988
+
Methods may also be declared as properties as by :external:py:class:`property` using the ``^:property`` metadata on the method name.
989
+
Property methods must be single arity.
990
+
991
+
Given a new type ``deftype`` named ``Point``, a new constructor function ``->Point`` will be created alongside the record type which accepts the full set of declared fields in the order they are declared.
992
+
993
+
.. note::
994
+
995
+
Method definitions must include a ``self`` or ``this`` parameter.
996
+
997
+
.. note::
998
+
999
+
Methods support tail recursion via :lpy:form:`recur`.
1000
+
When recurring, users should *not* pass the ``this`` or ``self`` parameter.
1001
+
1002
+
.. _reify:
1003
+
1004
+
``reify``
1005
+
^^^^^^^^^
1006
+
1007
+
Whereas :ref:`deftype` defines a true Python class which may be instantiated directly, :lpy:fn:`reify` defines an anonymous type implementing the named interfaces and protocols and returns an instance of that type immediately.
1008
+
Types defined via ``reify`` may not include fields.
1009
+
Instead, reified types close over their environment, which can provide many of the same benefits as fields.
1010
+
1011
+
Reify is likely to be most useful for creating one-off types implementing some Python type, rather than for creating types that are going to be created and used frequently by consumers of your code.
1012
+
1013
+
Reified types always implement :py:class:`basilisp.lang.interfaces.IWithMeta` and any metadata applied to the ``reify`` form are transferred to the created object.
1014
+
1015
+
.. note::
1016
+
1017
+
While ``reify`` and ``deftype`` are broadly similar, ``reify`` types may not define class or static methods.
1018
+
1019
+
.. _defrecord:
1020
+
1021
+
``defrecord``
1022
+
^^^^^^^^^^^^^
1023
+
1024
+
Basilisp offers a record type, created via :lpy:fn:`defrecord`, which is broadly similar to the types created by :ref:`deftype`.
1025
+
Record types are designed to be object types which can interact more readily with the core Basilisp library as a result of implementing the map interface directly.
1026
+
Records may be created from maps and fields in may be accessed, updated, and removed using standard :ref:`map <maps>` library functions.
1027
+
1028
+
There are some key differences from ``deftype`` types, however.
1029
+
1030
+
- Record types automatically implement :py:class:`basilisp.lang.interfaces.IPersistentMap`, :py:class:`basilisp.lang.interfaces.IWithMeta`, :py:class:`basilisp.lang.interfaces.IRecord`, and support for equality and hashing implemented via Python ``object`` methods.
1031
+
- ``defrecord`` fields may not be marked ``^:mutable``, nor may they provide a default via ``^:default``.
1032
+
- Types created by ``defrecord`` may not include :external:py:func:`classmethod`, :external:py:class:`property`, or :external:py:func:`staticmethod` methods.
1033
+
- Given a defrecord type ``Point``, a constructor function ``map->Point`` will be created alongside the record type which can construct a new ``Point`` record from a map in addition to the positional constructor ``->Point``.
1034
+
- Record literals may be constructed using their fully-qualified name as a :ref:`data reader <data_readers>` using a vector literal for a positional constructor or a map for a map based constructor.
Copy file name to clipboardExpand all lines: docs/pyinterop.rst
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -212,6 +212,8 @@ The ``:collect`` strategy is a better accompaniment to functions with positional
212
212
With this strategy, Python keyword arguments are converted into a Basilisp map with de-munged keyword arguments and passed as the final positional argument of the function.
213
213
You can use :ref:`associative_destructuring` on this final positional argument, just as you would with the map in the ``:apply`` case above.
0 commit comments