Skip to content

Latest commit

 

History

History
173 lines (129 loc) · 4.94 KB

behaviours.livemd

File metadata and controls

173 lines (129 loc) · 4.94 KB

Behaviours

Mix.install([
  {:youtube, github: "brooklinjazz/youtube"},
  {:hidden_cell, github: "brooklinjazz/hidden_cell"},
  {:tested_cell, github: "brooklinjazz/tested_cell"},
  {:utils, path: "#{__DIR__}/../utils"}
])

Navigation

Return Home Report An Issue

Setup

Ensure you type the ea keyboard shortcut to evaluate all Elixir cells before starting. Alternatively, you can evaluate the Elixir cells as you read.

Behaviours

Behaviours allow us to create modules that all share a common pattern but need different implementations.

You can define a behaviour, and then two other modules will implement it.

flowchart
  A[Behaviour] --> B[Module]
  A[Behaviour] --> C[Module]
Loading

This enforces that both modules will implement a shared application programming interface or API. What is an API? Well, in this specific context the API is the set of public-facing functions in a module. More generically an interface is the point at which two systems interact. Since we're talking about systems for programming applications, we call it the application programming interface.

To define a behaviour, we start by defining a module. However, instead of implementing functions, we create @callback module attributes. These @callback module attributes define the signature of a function.

The signature of a function is the name, params, and expected return value, but not the implementation. Essentially, it's the expected input, and output types in our program, but not the actual implementation.

To define the expected return value we use :: and then the data type we expect to return. Elixir provides a set of types. You can see the full list of types in the Typespecs documentation. For now, we're going to use the String.t() type. Some other common types are atom(), boolean(), integer(), float(), and any() if you don't require a specific output.

defmodule Villain do
  @callback catchphrase() :: String.t()
end

Now a module can use the Villain behaviour using the @behaviour module attribute. You'll notice that Elixir displays a warning because the DarthVader module is supposed to implement a catchphrase/0 function.

defmodule DarthVader do
  @behaviour Villain
end

To remove the warning, we need to implement my_function/0 and it should return a string.

defmodule DarthVader do
  @behaviour Villain
  def catchphrase do
    "No, I am your father."
  end
end

DarthVader.catchphrase()

So, why use a behaviour?

Primarily, behaviours ensure that any module that implements the behaviour provides the expected set of functions and that those functions match the expected function signatures.

Behaviours are another way to achieve polymorphism. We can define a behaviour, and then several different modules can implement that behaviour. Behaviours allow us to create "templates" for modules to ensure each module is consistent.

The caller, which is the place in the code where we call a module's function, can then have a consistent experience between modules.

flowchart
  A --> C
  A --> D
  A --> E
  A --> F
  C --> B
  D --> B
  E --> B
  F --> B

  A[Caller]
  B[Behaviour]
  C[Implementation]
  D[Implementation]
  E[Implementation]
  F[Implementation]
Loading

If the behaviour ever needs to change, we can modify the behaviour, and know which modules need to be updated thanks to the warnings Elixir provides.

You can also implement behaviours with dynamic dispatching which allows the caller to deal with a single module, and swap out the implementation.

flowchart
  A --> B

  B --> C
  B --> D
  B --> E
  B --> F
  A[Caller]
  B[Behaviour]
  C[Implementation]
  D[Implementation]
  E[Implementation]
  F[Implementation]
Loading

But that's beyond the scope of this lesson.

Your Turn

In the Elixir cell below, convert the Pet module into a behavior. The Pet behavior should define an @callback for a speak/0 function that returns a string.

defmodule Pet do
end

Once complete, re-evaluate the following module. It should display a warning.

speak/0 required by behaviour Pet is not implemented (in module Dog)

Add a speak/0 function to the Dog module. When you re-evaluate the cell, the warning should be gone.

defmodule Dog do
  @behaviour Pet
end

Commit Your Progress

Run the following in your command line from the beta_curriculum folder to track and save your progress in a Git commit.

$ git add .
$ git commit -m "finish behaviours section"