Home Posts Post Search Tag Search

Advanced Functional Elixir - 05 - Act on It
Published on: 2026-03-20 Tags: elixir, Blog, Testing, Advanced Functional Programming, FunPark, Act on It, Monoids, curry, predicates

Act on It

So now we want to add in more functionality for the Ride and Patron.


For a Patron context we want to have a recommended that will check for rides to avoid, and rides they want to ride on. It will use the tags from Patron.likes and Patron.dislikes.


# lib/fun_park/patron.ex
  def get_likes(%__MODULE__{likes: likes} = patron), do: likes
  def get_dislikes(%__MODULE__{dislikes: dislikes} = patron), do: dislikes

  def add_likes(%__MODULE__{} = patron, likes) when is_list(likes) do
    updated_likes = List.union(likes, get_likes(patron))
    updated_dislikes = List.difference(get_dislikes(patron), likes)

    change(patron, %{likes: updated_likes, dislikes: updated_dislikes})
  end

  def add_dislikes(%__MODULE__{} = patron, dislikes) when is_list(dislikes)do
    updated_likes = List.union(dislikes, get_dislikes(patron))
    updated_dislikes = List.difference(get_likes(patron), dislikes)

    change(patron, %{likes: updated_likes, dislikes: updated_dislikes})
  end

  def remove_likes(%__MODULE__{} = patron, likes) when is_list(likes) do
    updated_likes = List.difference(get_likes(patron), likes)

    change.(patron, %{likes: updated_likes})
  end

  def remove_dislikes(%{__MODULE__} = patron, dislikes) when is_list(dislikes) do
    updated_dislikes = List.difference(get_dislikes(patron, dislikes))

    change(patron, %{dislikes: dislikes})
  end

  def likes_ride(%__MODULE__{likes: likes}, %Ride{} = ride) do
    Ride.has_any_tag?(ride, dislikes)
  end

  def dislikes_ride(%__MODULE__{dislikes: dislikes}, %Ride{} = ride) do
    Ride.has_any_tag(ride, dislikes)
  end

  def recommended?(%__MODULE__{} = patron, %Ride{} = ride) do
    p_all([
      curry(&likes_ride?/2).(patron),
      p_not(curry(&dislikes_ride?/2).(patron)),
      curry(&Ride.suggested?/2).(patron)
    ]).(ride)
  end

For the Ride context, not much will change we will need to check against online? and not long_wait? which we already do.


So I added in some functionality for this new set of codes that will work to add and remove dislikes, also added in some functionality that will help to determine if there is any tags in the ride that person wants to ride. Let’s test it out.

Test It

iex(26)> family_ride = FunPark.Ride.make("family_ride", [min_age: 10, min_height: 40, wait_time: 40, online: true, tags: [:family]])
%FunPark.Ride{
  id: 143,
  name: "family_ride",
  min_age: 10,
  min_height: 40,
  wait_time: 40,
  online: true,
  tags: [:family]
}
iex(27)> scary_ride = FunPark.Ride.make("scary_ride", [min_age: 10, min_height: 40, wait_time: 40, online: true, tags: [:scary]])
%FunPark.Ride{
  id: 207,
  name: "scary_ride",
  min_age: 10,
  min_height: 40,
  wait_time: 40,
  online: true,
  tags: [:scary]
}
iex(28)> long_ride = FunPark.Ride.make("long_ride", [min_age: 10, min_height: 40, wait_time: 110, online: true, tags: []])
%FunPark.Ride{
  id: 78,
  name: "long_ride",
  min_age: 10,
  min_height: 40,
  wait_time: 110,
  online: true,
  tags: []
}
iex(29)> beth = FunPark.Patron.make("beth", 15, 50, [likes: [:family], dislikes: [:scary]])
%FunPark.Patron{
  id: 142,
  name: "beth",
  age: 15,
  height: 50,
  ticket_tier: :basic,
  fast_passes: [],
  reward_points: 0,
  likes: [:family],
  dislikes: [:scary]
}
iex(30)> alice = FunPark.Patron.make("alice", 15, 50, [likes: [:scary], dislikes: [:family]])
%FunPark.Patron{
  id: 206,
  name: "alice",
  age: 15,
  height: 50,
  ticket_tier: :basic,
  fast_passes: [],
  reward_points: 0,
  likes: [:scary],
  dislikes: [:family]
}
iex(31)> too_short = FunPark.Patron.make("2 short", 15, 20, [likes: [:scary, :family]])
%FunPark.Patron{
  id: 270,
  name: "2 short",
  age: 15,
  height: 20,
  ticket_tier: :basic,
  fast_passes: [],
  reward_points: 0,
  likes: [:scary, :family],
  dislikes: []
}
ex(34)> rides = [
...(34)> scary_ride,
...(34)> family_ride,
...(34)> long_ride
...(34)> ]
[
  %FunPark.Ride{
    id: 207,
    name: "scary_ride",
    min_age: 10,
    min_height: 40,
    wait_time: 40,
    online: true,
    tags: [:scary]
  },
  %FunPark.Ride{
    id: 143,
    name: "family_ride",
    min_age: 10,
    min_height: 40,
    wait_time: 40,
    online: true,
    tags: [:family]
  },
  %FunPark.Ride{
    id: 78,
    name: "long_ride",
    min_age: 10,
    min_height: 40,
    wait_time: 110,
    online: true,
    tags: []
  }
]
iex(35)> FunPark.Patron.recommended?(alice, long_ride)
false
iex(36)> FunPark.Patron.recommended?(alice, scary_ride)
false
iex(37)> FunPark.Patron.recommended?(alice, family_ride)
false
iex(38)> FunPark.Patron.recommended?(beth, long_ride)
false
iex(39)> FunPark.Patron.recommended?(beth, scary_ride)
false
iex(40)> FunPark.Patron.recommended?(beth, family_ride)
false
iex(55)> family_ride = FunPark.Ride.change(family_ride, %{wait_time: 20})
%FunPark.Ride{
  id: 143,
  name: "family_ride",
  min_age: 10,
  min_height: 40,
  wait_time: 20,
  online: true,
  tags: [:family]
}
iex(56)> scary_ride = FunPark.Ride.change(scary_ride, %{wait_time: 20})
%FunPark.Ride{
  id: 207,
  name: "scary_ride",
  min_age: 10,
  min_height: 40,
  wait_time: 20,
  online: true,
  tags: [:scary]
}
iex(57)> FunPark.Patron.recommended?(beth, long_ride)
false
iex(58)> FunPark.Patron.recommended?(beth, family_ride)
true
iex(59)> FunPark.Patron.recommended?(beth, scary_ride)
false
iex(60)> FunPark.Patron.recommended?(alice, long_ride)
false
iex(61)> FunPark.Patron.recommended?(alice, family_ride)
false
iex(62)> FunPark.Patron.recommended?(alice, scary_ride)
true
iex(63)> FunPark.Patron.recommended?(too_short, long_ride)
false
iex(64)> FunPark.Patron.recommended?(too_short, family_ride)
false
iex(65)> FunPark.Patron.recommended?(too_short, scary_ride)
false

That works for all the rides tested singularly, let’s add in recommended_rides/2 that will test a list of rides.

  def recommended_rides(%___MODULE__{} = patron, rides) do
    Enum.filter(rides, &recommended?(patron, &1))
  end

Now we can test the new function

iex(68)> rides = [
...(68)> scary_ride,
...(68)> family_ride,
...(68)> long_ride
...(68)> ]
[
  %FunPark.Ride{
    id: 207,
    name: "scary_ride",
    min_age: 10,
    min_height: 40,
    wait_time: 20,
    online: true,
    tags: [:scary]
  },
  %FunPark.Ride{
    id: 143,
    name: "family_ride",
    min_age: 10,
    min_height: 40,
    wait_time: 20,
    online: true,
    tags: [:family]
  },
  %FunPark.Ride{
    id: 78,
    name: "long_ride",
    min_age: 10,
    min_height: 40,
    wait_time: 110,
    online: true,
    tags: []
  }
]
iex(69)> FunPark.Patron.recommended_rides(beth, rides)
[
  %FunPark.Ride{
    id: 143,
    name: "family_ride",
    min_age: 10,
    min_height: 40,
    wait_time: 20,
    online: true,
    tags: [:family]
  }
]
iex(70)> FunPark.Patron.recommended_rides(alice, rides)
[
  %FunPark.Ride{
    id: 207,
    name: "scary_ride",
    min_age: 10,
    min_height: 40,
    wait_time: 20,
    online: true,
    tags: [:scary]
  }
]
iex(71)> FunPark.Patron.recommended_rides(too_short, rides)
[]