Translating Swift Enum Type into Elixir
We explore how to adapt Swift's indirect enum feature, specifically a Result enum, into Elixir's functional paradigm. Perfect for developers looking to expand their understanding of both Swift and Elixir.
We can't find the internet
Attempting to reconnect
Something went wrong!
Hang in there while we get back on track
The ability to understand and translate concepts between languages is a valuable skill. Today, we’re focusing on a specific feature from Swift - the indirect
keyword in enums - and how to replicate its functionality in Elixir. The Swift code we’re working with is a Result enum
, which showcases the versatility of enums in Swift.
enum Result {
case player(score: Int)
case cpu(score: Int)
case tie
indirect case previous(Result)
}
In Swift, the indirect
keyword allows an enum case to be recursive. In our example, previous(Result)
can hold a Result
which might itself be a previous. This is particularly useful for representing state that has a notion of history or progression, common in many applications.
Elixir, being a functional language, does not have a direct equivalent to Swift’s enums. However, we can use tuples and pattern matching to achieve a similar result. Here’s how we might represent our Result enum in Elixir:
defmodule Result do
defstruct [:type, :score]
@types ~w(player cpu tie previous)a
def all_cases(), do: @types
def score(type, score \\ 0)
def score(type, score) when type in @types, do: put_score(type, score)
def score(type, _score), do: raise(ArgumentError, message: "You cannot use #{inspect(type)}")
defp put_score(:player, score), do: %__MODULE__{type: :player, score: score}
defp put_score(:cpu, score), do: %__MODULE__{type: :cpu, score: score}
defp put_score(:tie, score), do: %__MODULE__{type: :tie, score: score}
defp put_score(:previous, previous = %__MODULE__{type: type}) when type in @types, do: %__MODULE__{type: :previous, score: previous}
defp put_score(:previous, _previous), do: raise(ArgumentError, message: "You must pass in a previous Result that has a known type of :player, :cpu, :tie, :previous")
end
In this Elixir version, we define a Result
module with a struct. The struct has three fields: :type
, :score
, and :previous
. We then define functions for each case that would be an enum case in Swift. The previous
function takes a Result
struct and sets it as the :previous
field, mimicking the recursive behavior of Swift’s indirect
.
In this example you can see how this module would work
defmodule ResultTest do
use ExUnit.Case
describe "Result" do
test "creating player result" do
result = Result.score(:player, 10)
assert result.type == :player
assert result.score == 10
end
test "creating cpu result" do
result = Result.score(:cpu, 20)
assert result.type == :cpu
assert result.score == 20
end
test "creating tie result" do
result = Result.score(:tie)
assert result.type == :tie
assert result.score == 0
end
test "creating previous result" do
previous_result = Result.score(:player, 10)
result = Result.score(:previous, previous_result)
assert result.type == :previous
assert result.score == previous_result
end
test "raising error for invalid type" do
assert_raise ArgumentError, fn -> Result.score(:foo) end
assert_raise ArgumentError, fn -> Result.score(:previous, 100) end
end
end
end
One of the strengths of Elixir is its pattern matching capabilities. When using our Result
module, we can easily destructure and handle different cases:
def handle_result(%Result{type: :player, score: score}) do
# handle player result
end
def handle_result(%Result{type: :previous, previous: previous_result}) do
# handle previous result recursively
end
While Swift and Elixir have different paradigms and syntactical structures, the core concept of a recursive enum in Swift can be effectively translated to Elixir. By using structs and pattern matching, Elixir can mimic the behavior of Swift’s indirect
enums, allowing for similar functionality in both languages.
This exercise not only shows the power and flexibility of these languages but also underscores the importance of understanding core programming concepts that transcend language boundaries. Whether you’re a Swift developer learning Elixir or vice versa, grasping these concepts can greatly enhance your programming skills and broaden your toolkit.