The Kernel module is a big one, but it looks like our in-depth look at this module will be coming to an end. Today I'll be covering the remaining functions in the Kernel module that couldn't be grouped together nicely.

Kernel.../2

The Kernel.../2 function corresponds to the .. operator that defines a range.

iex> 1..5
1..5
iex> Kernel...(1, 5)
** (SyntaxError) iex:2: syntax error before: '...'

iex> Kernel. ..(1, 5)
1..5

IEx was unable to understand the triple dots, probably thinking it was an operator. Once I separated the function name with a space (something I didn't previously realize was possible), IEx understood that I was attempting to call ../2.

Kernel.=~/2

The Kernel.=~/2 corresponds to the regular expression matching operator (~=), which I had no idea even existed until I looked at what this function did. The ~= operator is a regular expression matching operator. This function appears to do the same thing as Regex.match?/2, and in fact it would not surprise if it ended up as a call to Regex.match?/2.

I'm going to repeat the examples of regular expression matching from lwm 6, where we discussed regular expressions.

iex> regex = ~r/[0-9]+\.[0-9]*/
~r/[0-9]+\.[0-9]*/
iex> Regex.match?(regex, "0")
false
iex> Regex.match?(regex, "0A")
false
iex> Regex.match?(regex, "0.")
true
iex> Regex.match?(regex, "0.4")
true
iex> Regex.match?(regex, "1230.4653")
true
iex> Regex.match?(regex, "1230.4e")
true
iex> Regex.match?(regex, "1230.e4")
true
iex> Regex.match?(regex, "a1230.e4")
true
iex> Regex.match?(regex, "a1230a.e4")
false

Here are the same examples using the =~ operator.

iex> "0" =~ ~r/[0-9]+\.[0-9]*/
false
iex> "0A" =~ ~r/[0-9]+\.[0-9]*/
false
iex> "0." =~ ~r/[0-9]+\.[0-9]*/
true
iex> "0.4" =~ ~r/[0-9]+\.[0-9]*/
true
iex> "1230.4653" =~ ~r/[0-9]+\.[0-9]*/
true
iex> "1230.4e" =~ ~r/[0-9]+\.[0-9]*/
true
iex> "1230.e4" =~ ~r/[0-9]+\.[0-9]*/
true
iex> "a1230.e4" =~ ~r/[0-9]+\.[0-9]*/
true
iex> "a1230a.e4" =~ ~r/[0-9]+\.[0-9]*/
false		

Kernel.function_exported?/3

The Kernel.function_exported?/3 function determines if a module is loaded that contains a particular function with a particular arity. I suspect that this is a function that is mainly used by the Elixir interpreter, and would be infrequently used in application code.

The first parameter is the name of the module, the second parameter is the name of the function (in the form of an atom), and the third parameter is the arity of the function.

iex> function_exported?(String, :length, 1)
true
iex> function_exported?(String, :length, 0)
false
iex> function_exported?(Enum, :reduce, 3)
true
iex> function_exported?(NonExistentModule, :reduce, 3)
false

Kernel.inspect/2

We've already encountered the Kernel.inspect/2 function. It's very useful for getting a string representation of data. I'm pretty sure it's what IEx uses to display the value of an expression.

The inspection process apparently involves the Inspect protocol, which I'm not familiar with. Like the other protocols we've seen (which I think are like interfaces), a data structure must implement certain functionality in order to properly implement the protocol. Apparently, we could conceivably create a custom inspection string for our data structures, although I don't know how to do that yet.

Here are a few examples of using the function.

iex> inspect([1, 2, 3, 4, 5])
"[1, 2, 3, 4, 5]"
iex> inspect(:ok)
":ok"
iex> inspect({"a", 3})
"{\"a\", 3}"
iex> inspect(&String.length/1)
"&String.length/1"
iex> func = fn x -> x > 7 end
#Function<6.99386804/1 in :erl_eval.expr/5>
iex> inspect(func)
"#Function<6.99386804/1 in :erl_eval.expr/5>"

There are also options you can pass to Kernel.inspect/2, which can control how things are displayed. You can even control the colors of various elements. See the option documentation for details.

Kernel.macro_exported?/3

The Kernel.macro_exported?/3 function is just like Kernel.function_exported?/3, but used for macros instead. I'm not all that familiar with macros in Elixir, so I just looked at the module documentation until I saw a "(macro)" indicator next to a function name, indicating that it's actually a macro.

iex> macro_exported?(Kernel, :match?, 2)
true
iex> macro_exported?(Kernel, :non_existent_macro, 1)
false
iex> macro_exported?(Kernel, :min, 2)
false

Kernel.min/2 is a function in the Kernel module, but it is not a macro, so Kernel.macro_exported?/3 returns false.

Kernel.make_ref/0

The Kernel.make_ref/0 function will return a reference that, according to the documentation, is "almost unique". It is apparently possible to produce a duplicate macro, but it will take 2^82 calls do get that to happen, which is a lot: more than I can imagine. To put it in perspective, if we created a million references a second, it would take over 153.7 billion years before we encountered a duplicate. That's unique enough for almost anyone.

Now, what is a reference, and what is it used for? I don't know. This is the first time I've heard of the concept of a "reference" in Elixir, but I guess it's needed for situations where a unique value is truly needed. Process IDs maybe?

I imagine that I'll find out more about references in the future, and I'll just have to be content for now knowing just a hint about them.

Kernel.match?/2

The Kernel.match?/2 function determines if an expression matches a pattern. This is reminiscent of Regex.match?/2, but instead of matching a string with a regular expression, it matches an Elixir pattern with an Elixir expression. This is the same kind of pattern matching that occurs in other places in Elixir such as case or function clauses.

Here are some examples.

iex> match?({_, 3, :token}, {1, 3, :token})
true
iex> match?({_, 3, :token}, {1, 2, 3})
false
iex> match?(%{name: _}, %{name: "Bob", status: :sleeping})
true
iex> match?(%{name: _}, %{name: "Stu", status: :sleeping})
true
iex> match?(%{name: _}, %{favorite_color: :blue, status: :sleeping})
false

Kernel.max/2

The Kernel.max/2 function determines which of two values represents the biggest value. The biggest value is returned from the function. The default Elixir ordering is used to determine which value is the biggest, so any data type can be used.

If the values are equal, the first value is returned.

iex> max(4, 10)
10
iex> max(4, -2)
4
iex> max("Bob", "Cthulu")
"Cthulu"
iex> max("Cthulu", "earthworm")
"earthworm"
iex> max(3, 3.0)
3
iex> max(3.0, 3)
3.0

Kernel.min/2

The Kernel.min/2 function determines which of two values represents the smallest value. The smallest value is returned from the function. The default Elixir ordering is used to determine which value is the smallest, so any data type can be used.

If the values are equal, the first value is returned.

iex> min(4, 10)
4
iex> min(4, -2)
-2
iex> min("Bob", "Cthulu")
"Bob"
iex> min("Cthulu", "earthworm")
"Cthulu"
iex> min(3, 3.0)
3
iex> min(3.0, 3)
3.0

Kernel.node/0

The Kernel.node/0 function returns an atom representing the local node. Now what is a node? Well, I don't know. I hadn't even heard of a node before this, but I assume that this is more advanced functionality I haven't learned about yet.

I'm starting to suspect that a node might be an instance of the Erlang VM running on a machine. I know that the scalability of the Erlang VM allows code to be run across multiple servers, so I'm thinking that a node might be an Erlang VM instance that can be joined together with other Erlang VM instances running on other machines to form some sort of cluster. That's just a guess though. I imagine I'll eventually learn more.

Here's what I get when I run Kernel.node/0 in IEx:

iex> node
:nonode@nohost		

This makes me think that I may be thinking in the right direction regarding what nodes are. By the look of the output, I'm going to guess that whatever node IEx is running under has not been configured to work with other nodes, hence no node name and no host name. In fact, from the format of this atom, it looks like you can run multiple nodes on a single machine. I'm not sure what the point of that is though, unless it will allow for some advantage in future scalability by being able move individual nodes to other machines with minimal reconfiguration. I'm really just speculating, so I'm going to keep this in mind as I learn more to see how close I am to the real answer.

Interestingly, this function can be used in a guard clause. I guess you can run different code for a function depending on which node the code resides on.

Kernel.node/1

The Kernel.node/1 function works a lot like Kernel.node/0 except that instead of giving you an atom for the local node, you pass it a pid, reference, or port (which we have not learned about yet), and it returns the corresponding node. I guess that allows you to figure where certain resources are located.

This function can also be used in a guard clause.

Kernel.struct/2

The Kernel.struct/2 function dynamically constructs or updates a struct. The first parameter is the struct (when updating) or the struct module (when creating) and the second argument is an enumerable of key-value tuples (map or keyword list, for example) that will be used to set the values in the struct. If any of the name-value pairs do not exist in the struct, they will be ignored.

This has the feeling of a function that is used by an interpreter, but it could also be used in application code as well. For example, it could be used for creating or updating structs that share common properties without knowing the exact struct at compile time.

#Define the %Person struct
iex> defmodule Person do
...> defstruct name: "", status: :nothing, age: 0
...> end
{:module, Person,
 <<70, 79, 82, 49, 0, 0, 5, 252, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 183,
   0, 0, 0, 18, 13, 69, 108, 105, 120, 105, 114, 46, 80, 101, 114, 115, 111,
   110, 8, 95, 95, 105, 110, 102, 111, 95, 95, ...>>,
 %Person{age: 0, name: "", status: :nothing}}
#Create a new person
iex> person = struct(Person, [name: "Bob", age: 7])
%Person{age: 7, name: "Bob", status: :nothing}
#Update an existing person. The ":status" key is updated, 
#but the ":favorite_color" key is ignored, since it doesn't 
#exist in the structure
iex> struct(person, [status: :sleeping, favorite_color: :blue])
%Person{age: 7, name: "Bob", status: :sleeping}

Kernel.struct!/2

The Kernel.struct!/2 function works the same way as Kernel.struct/2, but as the name suggests, it will throw an exception if any of the keys being set in the structure does not exist in the structure.

Here's an example.

#Define the %Person struct
iex> defmodule Person do
...> defstruct name: "", status: :nothing, age: 0
...> end
{:module, Person,
 <<70, 79, 82, 49, 0, 0, 5, 252, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 183,
   0, 0, 0, 18, 13, 69, 108, 105, 120, 105, 114, 46, 80, 101, 114, 115, 111,
   110, 8, 95, 95, 105, 110, 102, 111, 95, 95, ...>>,
 %Person{age: 0, name: "", status: :nothing}}
#Create a new person
iex> struct!(Person, [name: "Bob", age: 7])
%Person{age: 7, name: "Bob", status: :nothing}
#Update an existing person with only valid keys
iex> struct!(person, [status: :sleeping])
%Person{age: 7, name: "Bob", status: :sleeping}
#Update an existing person with an invalid key
iex> struct!(person, [status: :awake, favorite_color: :blue])
** (KeyError) key :favorite_color not found in: %Person{age: 7, name: "Bob", status: :awake}
    (stdlib) :maps.update(:favorite_color, :blue, %Person{age: 7, name: "Bob", status: :awake})
    (elixir) lib/kernel.ex:2112: anonymous fn/2 in Kernel.struct!/2
    (elixir) lib/enum.ex:1940: Enum."-reduce/3-lists^foldl/2-0-"/3

Kernel.to_charlist/1

The Kernel.to_charlist/1 function converts any data to a charlist that implements the List.Chars protocol, which I guess defines how something will be converted to a character list.

iex> to_charlist("Drummy Drum Drum")
'Drummy Drum Drum'
iex> to_charlist(34)
'34'
iex> to_charlist(:ok)
'ok'

Kernel.to_string/1

The Kernel.to_string/1 function converts any data to a string that implements the String.Chars protocol, which I guess defines how something will be converted to a string.

iex> to_string('This is a sentence.')
"This is a sentence."
iex> to_string(155)
"155"
iex> to_string(:error)
"error"

Guard Functions

That's it for the functions in the Kernel module. Roughly half of the functions in the Kernel module can be used in guard clauses and the other half cannot. Here is a list of the functions from the Kernel module that can be used in guard clauses.

  • !=/2
  • !==/2
  • */2
  • +/1
  • +/2
  • -/1
  • -/2
  • //2
  • </2
  • <=/2
  • ==/2
  • ===/2
  • /2

  • =/2

  • abs/1
  • and/2
  • binary_part/3
  • bit_size/1
  • byte_size/1
  • ceil/1
  • div/2
  • elem/2
  • floor/1
  • hd/1
  • in/2
  • is_atom/1
  • is_binary/1
  • is_bitstring/1
  • is_boolean/1
  • is_float/1
  • is_function/1
  • is_function/2
  • is_integer/1
  • is_list/1
  • is_map/1
  • is_nil/1
  • is_number/1
  • is_pid/1
  • is_port/1
  • is_reference/1
  • is_tuple/1
  • length/1
  • map_size/1
  • node/0
  • node/1
  • not/1
  • or/2
  • rem/2
  • round/1
  • self/0
  • tl/1
  • trunc/1
  • tuple_size/1

I believe that is all the functions that can be used in a guard clause. I don't believe functions from any other modules can be used in this way.