Typespec Drills
Mix.install([
{:jason, "~> 1.4"},
{:kino, "~> 0.8.0", override: true},
{:youtube, github: "brooklinjazz/youtube"},
{:hidden_cell, github: "brooklinjazz/hidden_cell"}
])
Navigation
Typespec Drills
Drills help you develop familiarity and muscle memory with syntax through repeated exercises. Unlike usual problems, Drills are not intended to develop problem solving skills, they are purely for developing comfort and speed.
This set of drills is for Typespecs. Follow the instructions for each drill and complete them as quickly as you can.
Review
You can define a function spec using the @spec module attribute. Each @spec includes the
function parameter types, and the return value type.
@spec add(integer(), integer()) :: integer()
def add(int1, int2) do
int1 + int2
end
You can define custom types using the @type module attribute. These custom types can be used inside and outside of the current module.
@type user :: %{
name: string(),
age: integer()
}
You can include multiple types using the | operator. This works for functions using @spec and in custom types.
@spec double(integer() | nil)
Function Specs
Add function @specs based on the function parameter names and return value.
Example Solution
defmodule FunctionSpecs do
@spec do_nothing :: nil
def do_nothing do
nil
end
@spec accept_and_return_anything(any()) :: any()
def accept_and_return_anything(anything) do
anything
end
@spec double(float()) :: float()
def double(float) when is_float(float) do
float * 2.0
end
@spec double(integer()) :: integer()
def double(integer) when is_integer(integer) do
integer * 2
end
@spec double(number()) :: number()
def double(number) do
number * 2
end
@spec add(integer(), integer()) :: integer()
def add(integer1, integer2) do
integer1 + integer2
end
@spec multiply(integer(), integer()) :: integer()
def multiply(integer1, integer2) do
integer1 * integer2
end
@spec divide(integer(), integer()) :: float()
def divide(integer1, integer2) do
integer1 / integer2
end
@spec rounded_divide(integer(), integer()) :: integer()
def rounded_divide(integer1, integer2) do
div(integer1, integer2)
end
@spec concat(String.t(), String.t()) :: String.t()
def concat(string1, string2) do
string1 <> string2
end
@spec to_string(integer()) :: String.t()
def to_string(integer) do
Integer.to_string(integer)
end
@spec merge(map(), map()) :: map()
def merge(map1, map2) do
Map.merge(map1, map2)
end
@spec get_or_empty(Keyword.t(), atom()) :: any()
def get_or_empty(keyword_list, atom_key) do
Keyword.get(keyword_list, atom_key, "")
end
@spec split_and_lowercase(String.t()) :: [String.t()]
def split_and_lowercase(string) do
string
|> String.downcase()
|> String.split("", trim: true)
end
@spec string_to_int(String.t()) :: integer()
def string_to_int(string) do
String.to_integer(string)
end
@spec integers_to_strings([integer()]) :: [String.t()]
def integers_to_strings(integers) do
Enum.map(integers, fn int -> Integer.to_string(int) end)
end
@spec one_to_two(1) :: 2
def one_to_two(1) do
2
end
end
defmodule FunctionSpecs do
def do_nothing do
nil
end
def accept_and_return_anything(anything) do
anything
end
def double(float) when is_float(float) do
float * 2.0
end
def double(integer) when is_integer(integer) do
integer * 2
end
def double(number) do
number * 2
end
def add(integer1, integer2) do
integer1 + integer2
end
def multiply(integer1, integer2) do
integer1 * integer2
end
def divide(integer1, integer2) do
integer1 / integer2
end
def rounded_divide(integer1, integer2) do
div(integer1, integer2)
end
def concat(string1, string2) do
string1 <> string2
end
def to_string(integer) do
Integer.to_string(integer)
end
def merge(map1, map2) do
Map.merge(map1, map2)
end
def get_or_empty(keyword_list, atom_key) do
Keyword.get(keyword_list, atom_key, "")
end
def split_and_lowercase(string) do
string
|> String.downcase()
|> String.split("", trim: true)
end
def string_to_int(string) do
String.to_integer(string)
end
def integers_to_strings(integers) do
Enum.map(integers, fn int -> Integer.to_string(int) end)
end
def one_to_two(1) do
2
end
end
Custom Types
Implement the following custom @types in the module below based on the comment describing them.
Example Solution
defmodule CustomTypes do
# a string or number
@type unparsed_number :: String.t() | number()
# a list of strings
@type strings :: [String.t()]
# a map with :title (string) and :content (string) keys.
@type book :: %{title: String.t(), content: String.t()}
# A map with :name (string) and `:books` (a list of books) keys.
@type author :: %{name: String.t(), books: [book()]}
end
defmodule CustomTypes do
# a string or number
@type unparsed_number
# a list of strings
@type strings
# a map with :title (string) and :content (string) keys.
@type book
# A map with :name (string) and `:books` (a list of books) keys.
@type author
end
Mark As Completed
file_name = Path.basename(Regex.replace(~r/#.+/, __ENV__.file, ""), ".livemd")
save_name =
case Path.basename(__DIR__) do
"reading" -> "typespec_drills_reading"
"exercises" -> "typespec_drills_exercise"
end
progress_path = __DIR__ <> "/../progress.json"
existing_progress = File.read!(progress_path) |> Jason.decode!()
default = Map.get(existing_progress, save_name, false)
form =
Kino.Control.form(
[
completed: input = Kino.Input.checkbox("Mark As Completed", default: default)
],
report_changes: true
)
Task.async(fn ->
for %{data: %{completed: completed}} <- Kino.Control.stream(form) do
File.write!(
progress_path,
Jason.encode!(Map.put(existing_progress, save_name, completed), pretty: true)
)
end
end)
form
Commit Your Progress
Run the following in your command line from the curriculum folder to track and save your progress in a Git commit.
Ensure that you do not already have undesired or unrelated changes by running git status or by checking the source control tab in Visual Studio Code.
$ git checkout -b typespec-drills-exercise
$ git add .
$ git commit -m "finish typespec drills exercise"
$ git push origin typespec-drills-exercise
Create a pull request from your typespec-drills-exercise branch to your solutions branch.
Please do not create a pull request to the DockYard Academy repository as this will spam our PR tracker.
DockYard Academy Students Only:
Notify your teacher by including @BrooklinJazz in your PR description to get feedback.
You (or your teacher) may merge your PR into your solutions branch after review.
If you are interested in joining the next academy cohort, sign up here to receive more news when it is available.
Up Next
| Previous | Next |
|---|---|
| Credo | Games: Documentation and Static Analysis |