{"id":11022,"date":"2025-01-26T20:05:30","date_gmt":"2025-01-26T20:05:30","guid":{"rendered":"https:\/\/putridparrot.com\/blog\/?p=11022"},"modified":"2025-01-26T20:05:30","modified_gmt":"2025-01-26T20:05:30","slug":"protocols-and-behaviours-in-elixir","status":"publish","type":"post","link":"https:\/\/putridparrot.com\/blog\/protocols-and-behaviours-in-elixir\/","title":{"rendered":"Protocols and Behaviours in Elixir"},"content":{"rendered":"<p>Protocols and Behaviours in Elixir are similar to interfaces in languages such as C#, Java etc.<\/p>\n<p>Protocols can be thought of as interfaces for data whereas behaviours are like interfaces for modules, let&#8217;s see what this really means&#8230;<\/p>\n<p><strong>Protocols<\/strong><\/p>\n<p>A protocol is available for a data type, so let&#8217;s assuming we want a toString function on several data types but we obviously cannot cover all possible types that may be created in the future, i.e a Person struct or the likes. We can define a protocol which can be applied to data types, like this&#8230;<\/p>\n<p>Let&#8217;s start by define the protocol<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\ndefprotocol Utils do\r\n  @spec toString(t) ::String.t()\r\n  def toString(value)\r\nend\r\n<\/pre>\n<p>Basically we&#8217;re declaring the specification for the protocol using the @spec annotation. This defines the inputs and outputs, taking any params the after the :: is the return type. Next we define the function.<\/p>\n<p>At this point we have now implementations, so let&#8217;s create a couple of implementations for a couple of the standard types, String and Integer<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\ndefimpl Utils, for: String  do\r\n  def toString(value), do: &quot;String: #{value}&quot;\r\nend\r\n\r\ndefimpl Utils, for: Integer  do\r\n  def toString(value), do: &quot;Integer: #{value}&quot;\r\nend\r\n<\/pre>\n<p>The <em>for<\/em> is followed by the data type supported by this implementation. So as you can see, we have a couple of simple implementation, but where protocols become more important is that we can now define the toString function on other types, let&#8217;s assume we have the Person struct from a previous post<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\ndefmodule Person do\r\n  @enforce_keys &#x5B;:firstName, :lastName]\r\n  defstruct &#x5B;:age, :firstName, :lastName]\r\n\r\n  def create() do\r\n    %Person{ firstName: &quot;Scooby&quot;, lastName: &quot;Doo&quot;, age: 30 }\r\n  end\r\nend\r\n<\/pre>\n<p>and we want to give it a toString function, we would simply define a new implementation of the protocol for the Person data type, like this<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\ndefimpl Utils, for: Person  do\r\n  def toString(value), do: &quot;Person: #{value.firstName} #{value.lastName}&quot;\r\nend\r\n<\/pre>\n<p>Now from iex or your code you can do sometihing like this<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nscooby = Parson.create()\r\nUtils.toString(scooby)\r\n<\/pre>\n<p>and you&#8217;ve got toString working with the Person type.<\/p>\n<p><strong>Behaviours<\/strong><\/p>\n<p>Behaviours are again similar to interfaces but are used to define what a module is expected to implement. Let&#8217;s stick with the idea of a toString function which just outputs some information about the module that&#8217;s implementing it, but this time we&#8217;re expecting a module to implement this function, so we declare the behaviour as follows<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\ndefmodule UtilBehaviour do\r\n  @callback toString() :: String.t()\r\nend\r\n<\/pre>\n<p>We use the @callback annotation to declare the expected function(s) and @macrocallback for macros. As per the protocol we give the signature of the function followed by :: and the expected return type.<\/p>\n<p>Now to implement this, let&#8217;s again go to our Person struct (remember this version of toString is just going to output some predefined string that represents the module)<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\ndefmodule Person do\r\n  @behaviour UtilBehaviour\r\n\r\n  @enforce_keys &#x5B;:firstName, :lastName]\r\n  defstruct &#x5B;:age, :firstName, :lastName]\r\n\r\n  def create() do\r\n    %Person{ firstName: &quot;Scooby&quot;, lastName: &quot;Doo&quot;, age: 30 }\r\n  end\r\n\r\n  def toString() do\r\n    &quot;This is a Person module\/struct&quot;\r\n  end\r\nend\r\n<\/pre>\n<p>Now our module implements the behaviour and using <em>Person.toString()<\/em> outputs &#8220;This is a Person module\/struct&#8221;.<\/p>\n<p>We can also use the @impl annotation to ensure that you explicitly define the behaviour being implement like this<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n@impl UtilBehaviour\r\ndef toString() do\r\n  &quot;This is a Person module\/struct&quot;\r\nend\r\n<\/pre>\n<p>This @impl annotation tells the compiler explicitly what you&#8217;re implementing, this is just an aid to development by making it clear what&#8217;s implementing what. If you use @impl once you have to use it on every behaviour.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Protocols and Behaviours in Elixir are similar to interfaces in languages such as C#, Java etc. Protocols can be thought of as interfaces for data whereas behaviours are like interfaces for modules, let&#8217;s see what this really means&#8230; Protocols A protocol is available for a data type, so let&#8217;s assuming we want a toString function [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[730],"tags":[],"class_list":["post-11022","post","type-post","status-publish","format-standard","hentry","category-elixir"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/11022","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/comments?post=11022"}],"version-history":[{"count":4,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/11022\/revisions"}],"predecessor-version":[{"id":11273,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/11022\/revisions\/11273"}],"wp:attachment":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/media?parent=11022"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/categories?post=11022"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/tags?post=11022"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}