> ## Content Index
> Fetch the complete content index at: https://inquisitivedeveloper.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Learn With Me: Elixir - Modules and Functions: Part 2 (#11)
- URL: https://inquisitivedeveloper.com/lwm-elixir-11/
- Published: 2018-11-22T16:00:00.000Z
- Updated: 2018-12-12T18:22:24.000Z
- Description: More about Elixir modules and functions. It's a big topic, and someone has to cover it.
- Author: Kevin Peter
- Tags: Learn With Me: Elixir, Elixir, #Import 2026-10-23 20:03

Part 1 of this topic introduced modules, functions, and how to call them. There's a lot to talk about with functions in Elixir, so let's continue.

## Default Parameter Values

Elixir function parameters can be given default values using `\\` after each parameter.

```elixir
def do_something(item, count \\ 1)

```

So in the above function, if you don't specify a `count` parameter when calling it, the `count` parameter will default to 1\. This means you can call `do_something("Bob")`, in which case `count` will default to 1 or `do_something("Bob", 4)`, in which case `count` will get a value of 4.

Elixir will actually generate multiple function definitions when it sees default values. In this case, it generates `do_something/1` and `do_something/2`. The function with an arity of 1 will just call the function with an arity of 2, passing it the default value.

That was all the information I found when learning about default parameter values, so I set out to learn about how default values differ compared to Javascript and C#.

In Javascript, all parameters are optional. If you specify a default value for a parameter (ES6 and later), that default value will be assigned to the parameter if one wasn't specified by the caller. If not, then that parameter will get the value `undefined`.

Let's look at the following function in Javascript.

```javascript
function do_something(a, b = 1, c, d) {
  return [a, b, c, d];
}

```

Calling `do_something("Bob")` will return `["Bob", 1, undefined, undefined]`. The parameters that had neither default values nor had values supplied by the caller will default to `undefined`.

In C#, on the other hand, all parameters with default values have to be at the end of the parameter list. A parameter with a default value, which is an optional parameter in C#, can never come before a required parameter (one that has no default value).

So the following example would result in a compile error.

```csharp
public List<int> DoSomething(int a, int b = 1, int c, int d)
{
	return new List<int> {a, b, c, d};
}

```

Moving the default value to the end would allow the code to compile.

```csharp
public List<int> DoSomething(int a, int b, int c, int d = 1)
{
	return new List<int> {a, b, c, d};
}

```

I didn't find any indication of how Elixir would behave in this situation after about 30 seconds of searching, so I decided to try it out to see what Elixir would do. Elixir didn't do what I would expect.

I created the following function in Elixir

```elixir
def create_list(a, b \\ 1, c, d) do
	[a, b, c, d]
end

```

I then called it with three parameters. My expectation was that it would fill in the first three (a, b, c), and then give me an error because it didn't have a value for d. Instead it went like this:

```elixir
iex> DefaultValues.create_list("Bob", 4, 3)
{"Bob", 1, 4, 3}
iex> DefaultValues.create_list("Bob", :ok, 4, 3)
{"Bob", :ok, 4, 3}

```

It turns out that Elixir generated a `do_something/3` function and a `do_something/4` function. `do_something/3` only took the required parameters and called `do_something/4` with the default value for the second parameter. That was very interesting. It has the effect that we can put the default values anywhere in the parameter list and then only pass the required parameters, regardless of where they occur in the parameter list. The required parameters will be filled first.

It makes sense once I realize that Elixir generates alternate function definitions that call the real definition with the default values. It's irrelevant where the optional parameters are located.

## Private Functions

A private function is a function in a module that is only visible within the module. It cannot be be referenced or called from outside the module. Whereas public functions are defined using the `def` keyword, private functions are defined using the `defp` keyword.

```elixir
defmodule ExampleModule do
	defp private_function(value) do
		
	end
end

```

## Function and Module Naming Conventions

Functions and modules have naming conventions that every Elixir developer uses. Elixir won't get upset if you go outside those conventions, but other Elixir developers will find it more difficult to read and understand your code.

Like variables, functions should be snake cased. This where they are named using all lower-case letters with underscores beween the words, like `do_something_unexpected`.

Like with a variable, a function name that begins with an underscore (\_) indicates that a function should not be used. I'm not sure why functions are defined this way rather than being defined as private functions, but there must be some valid uses cases I'm not aware of. Perhaps it's done this way so that one particular module can use it (a bit like the concept of friends in C++), but not other modules that have no business doing so? I'd be interested in seeing an example of this and the reasoning for it.

A "?" character is typically added to the end of a function name when it returns a boolean value. For example: `is_available?`. An exception to this are functions that can be used in guard clauses. Those functions return a boolean variable, but do not have a "?" at the end of the function name. I don't know what guard clauses are at this point, but it's something I'll learn in the future.

A "!" character is typically added to the end of a function if it raises an exception when there's a failure. Many functions in the Elixir I/O libraries have a version of a function that raises an exception on failure and a version of the same function that does not raise an exception, with the difference between the two functions being the "!" character at the end of the function name.

For example, `File.write/3` and `File.write!/3`.

The functions that do not throw an exception will typically return a tuple that contains the result of the operation (`:ok` or `:error`) and any data that resulted from the operation.

When "size" appears in an Elixir function name, that indicates that the operation runs in constant time (O(1)). This is usually because the size is stored with the data structure.

When "length" appears in an Elixir function name, that indicates that the operation runs in linear time (O(n)). This is usually because the algorithm has to iterate over the data items to figure out how many there are.

When a function *parameter* will not be used, and underscore is placed in front of the name. It's common to have several versions of a function where a parameter is used in one version, but not another. Not only does the underscore impart information to the human reading the code, but it also suppresses a compile warning that is emitted when a function does not use a parameter.

An example warning when the compiler detects an unused parameter:

```
warning: variable "param2" is unused
iex:4

```

By putting an underscore before the unused parameter(s), this warning can be avoided.

```elixir
def do_something(param1, _param2) do
	param1
end

```

Modules are named using camel case, where the first letter is a capital letter and each word in the module starts with a capital letter.

```elixir
defmodule RoadSegment do

end

```

## Using Functions from Another Module

Calling a function in another module can simply be done by calling "\[Module Name\].\[Function Name\]".

Here's an example of calling the `length` function in the `String` module. The String module is part of the Elixir standard library, and it appears that all standard library modules are available to use everywhere.

```elixir
iex> String.length("This is a string")
16

```

At this point, it's not yet clear to me how Elixir finds the module you referenced. Is there some kind of search path or are all the modules that have been compiled by the Elixir compiler available for use? I don't know. I expect that will become more clear in the future.

## Importing Entire Modules

If you want to import the functions from a module into the current namespace, you can use the `import` keyword. The following example imports all the functions from the String module into the current namespace.

```elixir
iex> import String
String
iex> String.upcase("This is a string")
"THIS IS A STRING"
iex> upcase("This is a string")
"THIS IS A STRING"

```

Importing a function means that you don't have to specify its module name anymore. You can just call its function name.

The functions that are available in Elixir without specifying a module name or importing anything are located in the `Kernel` module. These are functions such as `is_atom` or `div`. Even operators such as `+`, `-`, `&&` or `===` are actually functions in the `Kernel` module. These are available without specifying `Kernel.+` or `Kernel.is_atom` because the Elixir environment automatically imports all the functions in `Kernel`.

You'll need to be careful because the names of functions in a module that were imported into the current namespace can conflict with functions from other modules that were imported.

For example, the `Kernel` module has a function called `length/1` that determines the length of a list. Why this is not in the List module instead is a mystery to me, but that's the way it is. (I notice that the documentation indicates that this can be used in guard clauses, whatever those are, so I wonder if that's the reason). The `String` module also has a function called `length/1`. I noticed that importing all the functions in String didn't cause any kind of error or warning, yet we should now have two `length/1` functions in the current namespace.

Let's see what happens when we try to call `length/1` on a string.

```elixir
iex> import String
String
iex> length("This is a string")
** (CompileError) iex:7: function length/1 imported from both String and Kernel, call is ambiguous
    (elixir) src/elixir_dispatch.erl:111: :elixir_dispatch.expand_import/6
    (elixir) src/elixir_dispatch.erl:81: :elixir_dispatch.dispatch_import/5
iex> String.length("This is a string")
16
iex> Kernel.length([1, 2, 3])
3

```

That is interesting. Elixir complains that the call is ambiguous because both `String.length/1` and `Kernel.length/1` have been imported into the current namespace. We have to disambiguate them by specifying the module name.

Note that Elixir isn't like Python, where you have to use `import` in order to load and use another module. It's more like the `using` statement in C#, which just brings code from another namespace into the current namespace. Like C#, Elixir code can still still use that other code without any sort of `import` statement, but importing it will allow the developer to refer to that code without typing in the entire namespace.

Any functions in a moduel starting with an underscore will not be automatically imported, because by convention, these are functions that should not be used. If you must use them anyway, you have to specifically import them by name.

### Importing Specific Functions from a Module

Elixir guidelines discourage importing all the functions in a module due to the likelihood of function name conflicts and creating unclear code where someone who's reading the code isn't certain which module a particular function actually came from. Instead, the guidelines recommend either specifying the module name or importing specific functions by name from modules, which will make it clear exactly which functions are being imported and which module they are being imported from.

We can import specific functions from a module using `only:` syntax. Let's look at an example where we import a couple functions from the `String` modules.

```elixir
iex> import String, only: [upcase: 1, trim: 1]
String
iex> upcase("This is a string")
"THIS IS A STRING"
iex> trim("  This is a string  ")
"This is a string"
iex> split("This is a string")
** (CompileError) iex:14: undefined function split/1

iex> String.split("This is a string")
["This", "is", "a", "string"]
iex> length([1, 2, 3])
3

```

In this example, we imported only `upcase/1` and `trim/1` from the String modules. The `upcase: 1` value in the import statement corresponds to `upcase/1`. We were then able to use both the imported functions, but another function in the `String` module that was not imported, `String.split/1`, is still unavailable in the current namespace.

Importing specific functions from `String` avoids the `length/1` function name conflict, because we didn't import `String.length/1`. Importing functions this way also makes it clear where the `upcase/1` and `trim/1` functions came from.

There's also `except:`, which is used in the same context as `only:`, but with the opposite effect. It imports all functions *except* for the ones listed.

### Other Ways of Importing

Like the "using" statement in C#, you can create a namespace alias instead of importing a module into the current namespace. This is done with the `alias` keyword.

```elixir
iex> alias String, as: Bob
String
iex> Bob
String
iex> Bob.length("This is a string")
16
iex> Bob.upcase("This is a string")
"THIS IS A STRING"
iex> String.upcase("This is a string")
"THIS IS A STRING"

```

In this example, I created an alias called `Bob` that is an alias for the `String` module.

You can use `alias` to simplify long namespaces, so a namespace such as `NameSpace1.Namespace2.Category.Subdivision.ExampleModule` can be aliased to `ExampleModule`. This makes it a lot easier to refer to a function in that module without importing its functions into the current namespace.

If you use `alias` without the `as:`, the alias will be set to the last part of the module name. So `alias NameSpace1.Namespace2.Category.Subdivision.ExampleModule` without `as:` would automatically be aliased to `ExampleModule`, since that is the last part of the module name following the last dot.

Both an alias and an import are lexically-scoped, so you can put aliases and imports at the file level, inside modules, or inside individual functions if you want to do so. An alias or import contained within a function would only apply in the scope of that function and be unavailable in other functions in the same module.

```elixir
defmodule ExampleModule do
	def atom_to_string_one(atom) do
		Atom.to_string(atom)
	end
	
	def atom_to_string_two(atom) do
		import Atom, only: [to_string: 1]
		to_string(atom)
	end
	
	def atom_to_string_three(atom) do
		alias Atom, as: Waffle
		Waffle.to_string(atom)
	end	
end

```

There is a `use` macro that will import a module's functions into the current namespace, but also runs some code in that module that will do some setup work that needs to be done before the module is used. At this point, I don't know exactly what this involves, how to use it, or even what a macro is in the context of Elixir, but I do know that it is a tool that is available. I expect to learn more about the `use` macro, and macros in general, once I get into learning the more advanced functionality.

## Module Attributes

A module attribute represents an annotation containing some kind of metadata or data that can be used as a constant. A module attribute begins with an "@" character and a lower-case letter.

```elixir
defmodule ExampleModule do
	@test_file_name "test_file.txt"
	
	def test_file() do
		@test_file_name
	end
end

```

This example defines an attribute called `@test_file_name` and assigns it a value `"test_file.txt"`. This attribute can then be used anywhere within the module. Here I use the attribute like a constant. The attribute doesn't appear to be available outside the module.

```elixir
iex> ExampleModule.test_file
"test_file.txt"
iex> ExampleModule.@test_file_name
** (CompileError) iex:23: undefined function test_file_name/0

```

Module attributes can be redefined and the compiler will use whichever value is currently assigned as it goes down through the code. This also means that the same attribute can be reused for metadata purposes without causing trouble.

```elixir
defmodule ExampleModule do
	@test_file_name "test_file.txt"
	
	def test_file() do
		@test_file_name
	end
	
	@test_file_name "another_test_file.txt"
	
	def second_test_file(), do: @test_file_name
end

```

```
iex> ExampleModule.test_file
"test_file.txt"
iex> ExampleModule.second_test_file
"another_test_file.txt"

```

In these examples, I'm not using parentheses to make the function call. A function that just returns a constant feels more like a module constant to me (even though it's actually a function), so I'm just leaving off the parentheses. Elixir is just fine with that.

Modules attributes can only be defined at the module level, not inside functions.

I don't feel I've learned a lot about how attributes are used yet. I know that they're often used as annotations on modules and functions, but I suspect that I have yet to get a sense of how they are used in real Elixir code. I expect to learn more about how they are typically used later on when I look at real Elixir code.

I do know that module attributes are only available at compile time, and cannot be retrieved at runtime by querying metadata. So any code that uses an attribute value will have that value inserted during compilation.

## ElixirOperator - A Practical Example

I think I've learned enough now to do something practical. I learn best by taking what I've learned and doing something with it, so I'm going to build something simple now. I encourage you to take the time to do something similar. Do some coding with what you know so far from following along with me. Be curious and play around with Elixir. It will teach you much more than just reading this and doing nothing else.

You can find the ElixirOperator project in its own folder under the projects folder in the [Learn With Me: Elixir repository](https://github.com/Maultasche/LwmElixirCode?ref=inquisitivedeveloper.com) on Github. You can download the project and follow along.

First let's look at the `MathOperations` module found in math\_operations.exs.

```elixir
defmodule MathOperations do
	@pi 3.141592653589793
	
	def add(x, y), do: x + y
	def subtract(x, y), do: x - y
	def multiply(x, y), do: x * y
	def divide(x, y), do: x / y
	def mod(x, y), do: rem(x, y)
	def negate(x), do: -x
	def square(x), do: x * x
	def pi(), do: @pi
end

```

This module contains a variety of mathematical functions. The `add`, `subtract`, `multiply`, and `divide` functions are pretty straightforward. The `mod` function performs a modulo operation, which is represented by the `%` operator in C# and Javascript. The `negate` function takes a number and flips its sign, the `square` function squares a number, and the `pi` function simply returns the value of PI, which is stored in a module attribute. It's a pretty simple module overall, and all functions can be written as single-line functions.

Now let's load this module into IEx using the `c` function. The path is relative to the current directory. To make it easy, go to the ElixirOperator directory and start IEx.

```elixir
iex> c "math_operations.exs"
[MathOperations]

```

Now we have the module loaded into IEx and we can start calling functions.

```elixir
iex> MathOperations.add(10, 23)
33
iex> MathOperations.subtract(5, 15)
-10
iex> MathOperations.multiply(5, 3)
15
iex> MathOperations.divide(10, 3)
3.3333333333333335
iex> MathOperations.mod(8, 2)
0
iex> MathOperations.mod(8, 3)
2
iex> MathOperations.negate(9)
-9
iex> MathOperations.negate(-17)
17
iex> MathOperations.square(5)
25
iex> MathOperations.pi
3.141592653589793
iex> MathOperations.pi()
3.141592653589793

```

Great! I created a module with some useful math operations. It's not really that impressive, but it was a good exercise to get familiar with Elixir.

I also wanted to get some practice creating some higher-order functions, which led to me creating the Operator module. The functions in the operator module receive one or more functions and some data and call those functions with that data.

Let's take a look.

```elixir
defmodule Operator do
	#Takes some data and a function with one parameter and applies a function to that data
	def apply(operation, data) do
		operation.(data)
	end

	#Takes some data and a function with two parameters and applies a function to that data
	def apply(operation, data1, data2) do
		operation.(data1, data2)
	end
	
	#Takes some data and two functions, the first function with one parameter and the second function with 
	#one parameter and applies the first function to that data, with the second function fed the result
	#of the first function. The result of that will be returned.
	def apply_two(operation1, operation2, data) do
		result = operation1.(data)
		operation2.(result)
	end
	
	#Takes some data and two functions, the first function with two parameters and the second function with 
	#one parameter and applies the first function to that data, with the second function fed the result
	#of the first function. The result of that will be returned.
	def apply_two(operation1, operation2, data1, data2) do
		result = operation1.(data1, data2)
		operation2.(result)
	end
end

```

Yes, the same thing can be done by calling the functions directly, but this way I'll get some practice with implementing higher-order functions.

The apply functions will call a single function, passing it some data and returning the result. The apply\_two functions will pass the data to the first function, take the result of the first function and pass it to the second function, and then return the result of the second function. It's like a primitive, inflexible function pipeline.

initial data --> function1 --> function2 --> final result

See how we use dot notation in the Operator module to call the functions passed into us. This is because those functions are bound to a parameter and are not named functions. It's possible that the parameter variable may be bound to a named function, but it is not itself a named function.

Let's load the module into IEx and start using it in conjunction with the functions from the MathOperations module. This is an example of function composition where we can combine simpler functions to do something more complex.

```elixir
iex> c "operator.exs"
[Operator]
iex> Operator.apply(&MathOperations.add/2, 4, 9)
13
iex> Operator.apply(&MathOperations.subtract/2, 4, 9)
-5
iex> Operator.apply(&MathOperations.multiply/2, 4, 9)
36
iex> Operator.apply(&MathOperations.divide/2, 4, 9)
0.4444444444444444
iex> Operator.apply(&MathOperations.mod/2, 9, 4)
1
iex> Operator.apply(&MathOperations.negate/1, 9)
-9
iex> Operator.apply(&MathOperations.square/1, 9)
81
iex> Operator.apply(&MathOperations.square/1, MathOperations.pi)
9.869604401089358

```

Notice how we have to use the capture operator and the function's name/arity to pass a function to the apply method. In the last call, we passed the square/1 function and we called the pi/0 function to retrieve the data to be squared. The result was that PI was squared. If we had wanted to pass the `MathOperations.pi/0` function rather than the value it returns, we would have had to use the capture operator. That's the difference in syntax between passing a function and calling the function and passing its result.

We are also calling both versions of the apply function here. The calls with three parameters match `Operator.apply/3` and the calls with two parameters match `Operator.apply/2`.

Now let's try using the `apply_two` functions.

```elixir
iex> Operator.apply_two(&MathOperations.negate/1, &MathOperations.square/1, 8)
64
iex> Operator.apply_two(&MathOperations.square/1, &MathOperations.negate/1, 8)
-64
iex> Operator.apply_two(&MathOperations.add/2, &MathOperations.square/1, 5, 2)
49
iex> Operator.apply_two(&MathOperations.multiply/2, &MathOperations.square/1, 5, 2)
100

```

- In the first call, the number 8 is negated to -8 and then squared, which results in the value 64.
- In the second call, we reversed the order of operations so that 8 is squared to 64 and then negated, which results in -64.
- In the third call, 5 and 2 are added to get 7, which is then squared. The result is 49.
- In the fourth call, 5 and 2 are multiplied to get 10, which is then squared. The result is 100.

I've gotten the impression that functional programming is filled with higher-order functions that implement some kind of pattern and then more specific functions are passed into them as parameters. This is a simple example of that way of programming. I expect I'll get better at this way of thinking over time.

Before we finish, let's try passing some anonymous functions to these `apply` functions.

```elixir
iex> Operator.apply(fn (x, y) -> x - (y *2) end, 10, 3)
4
iex> Operator.apply(fn x -> x * (x + 1) end, 10)
110
iex> Operator.apply(&(&1 + (&2 * &1)), 10, 3)
40
iex> Operator.apply_two(&(&1 + (&2 * &1)), &MathOperations.negate/1, 10, 3)
-40

```

- The first call applies an anonymous function with two parameters
- The second call applies an anonymous function with one parameter
- The third call uses the shortcut syntax to create an anonymous function
- The fourth call applies an anonymous function and then uses the `&MathOperations.negate/1` function to negate the result.

Well, that little project taught me a lot. What I didn't show here are the various compile errors and the other errors IEx showed me while I attempting to call these functions. There were indeed plenty of errors, but I didn't think that would be very helpful to clutter up these code examples with a lot of errors, so I edited them out.

Here's the unedited version of that last example.

```elixir
iex> Operation.apply(fn (x, y) -> x - (y *2) end, 10, 3)
** (UndefinedFunctionError) function Operation.apply/3 is undefined (module Operation is not available)
    Operation.apply(#Function<12.99386804/2 in :erl_eval.expr/5>, 10, 3)
iex> Operator.apply(fn (x, y) -> x - (y *2) end, 10, 3)
4
iex> Operator.apply(fn x -> x * (x + 1), 10, 3)
** (SyntaxError) iex:69: unexpected token: ). The "fn" at line 69 is missing terminator "end"

iex> Operator.apply(fn x -> x * (x + 1) end, 10, 3)
** (BadArityError) #Function<6.99386804/1 in :erl_eval.expr/5> with arity 1 called with 2 arguments (10, 3)
    projects/ElixirOperator/operator.exs:9: Operator.apply/3
iex> Operator.apply(fn x -> x * (x + 1) end, 10)
110
iex> Operator.apply(&(&1 + (&2 * &1)), 10)
** (BadArityError) #Function<12.99386804/2 in :erl_eval.expr/5> with arity 2 called with 1 argument (10)
    projects/ElixirOperator/operator.exs:4: Operator.apply/2
iex> Operator.apply(&(&1 + (&2 * &1)), 10, 3)
40
iex> Operator.apply(&(&1 + (&2 * &1)), &MathOperations.negate/1, 10, 3)
** (UndefinedFunctionError) function Operator.apply/4 is undefined or private. Did you mean one of:

      * apply/2
      * apply/3

    Operator.apply(#Function<12.99386804/2 in :erl_eval.expr/5>, &MathOperations.negate/1, 10, 3)
iex> Operator.apply_two(&(&1 + (&2 * &1)), &MathOperations.negate/1, 10, 3)
-40
iex>

```

The first error was me getting the name of the `Operator` module wrong. The second error was me forgetting to terminate the anonymous function with `end`. The third error was me applying two parameters to a function that only accepted one parameter. The fourth error was me attempting to apply one parameter to a function that accept two parameters. The fifth error was me passing two functions to the `apply` functions instead of the `apply_two` functions.

I want you to know that it's common to make a lot of mistakes while learning, and failing is all a part of learning! The process of stumbling around and causing errors and figuring what went wrong was an essential part of learning Elixir. It also helps gives you practice in interpreting Elixir error messages, making it much easier for you to figure out what went wrong in the future. As long as you learn something from your mistakes, it's not a waste of time.

I learned a lot more by doing this than if I had just kept reading about it. I encourage you to take a break from reading this at this point and play around with Elixir, using what we've learned so far. It's quite an effective way to learn.