jonoke

jonoke

Hi,

I’m just working my way into Elixir and loving it… it’s crazy how you can get complicated stuff to work first time that would NEVER come our right first time in a procedural.

So … no global variables.

I’m working on a Bridge hand dealer as my exploration path, so I need a deck of cards to start with. Normally I’d create a data structure and assign it to a global variable and use that throughout the application … but … no global variables.

So here’s what I came up with

  def deck do
    [
      {0, 0}, {0, 1}, {0, 2}, {0, 3}, {0, 4}, {0, 5}, {0, 6}, {0, 7}, {0, 8}, {0, 9}, {0, 10}, {0, 11}, {0, 12},
      {1, 0}, {1, 1}, {1, 2}, {1, 3}, {1, 4}, {1, 5}, {1, 6}, {1, 7}, {1, 8}, {1, 9}, {1, 10}, {1, 11}, {1, 12},
      {2, 0}, {2, 1}, {2, 2}, {2, 3}, {2, 4}, {2, 5}, {2, 6}, {2, 7}, {2, 8}, {2, 9}, {2, 10}, {2, 11}, {2, 12},
      {3, 0}, {3, 1}, {3, 2}, {3, 3}, {3, 4}, {3, 5}, {3, 6}, {3, 7}, {3, 8}, {3, 9}, {3, 10}, {3, 11}, {3, 12}
    ]
  end

then to deal a hand I have

  def deal do
    dealing(Enum.shuffle(deck()))
  end

where dealing splits the list into four hands.

I’m hoping I don’t get something … as this makes my procedural brain go nuts … I’m getting a new copy of the deck every time?

Jonathan.

Showing Posts 1 to 4

untra

untra

Yup :grin:
In the world of functional programming, everything is basically pass-by-value. Every call to deck() will return a new array containing 52 elements.

This can be both a good and bad thing. There’s not really a “heap” to think about allocating to hold the deck, and theres no reference passing to think about, either.

When Enum.shuffle(enumerable) is called, it will return an entirely new array, with the deck elements randomly shuffled.
When dealing(deck_of_cards) do is called, the four hands it returns are new copies as well.

functional programming encourages design like this. functions that take smaller inputs, and return consistent results with no side effects. In a sense, everything is pass-by-value. The result is software with (fewer) side effects, and (after some practice) is easier to grok and study.

also, try this:

heart = "H"
spade = "S"
diamond = "D"
club = "C"
suits = [heart, spade, diamond, club]
ranks = ~w(2 3 4 5 6 7 8 9 T J Q K A)
def deck do
  Enum.flat_map(suits, fn (suit) -> Enum.map(ranks, fn (rank) -> {suit, rank} end) end)
end
NobbZ

NobbZ

Instead of strings for the suits I’d use atoms, same for the ranks, which boils roughly to thhis:

defmodule Deck do
  # creates list of atoms for the suite
  @suits ~w[heart spade diamond club]a

  # creates list of ints and atoms according to a cards rank
  @ranks [2, 3, 4, 5, 6, 7, 8, 9, 10] ++ ~w[jack queen king ace]a

  # creates list of tuples of {suit, rank}
  @deck for s <- @suites, r <- @ranks, do: {r, s}

  @doc "returns a freshly unpacked deck in order"
  def fresh_deck(), do: @deck

  @doc "returns a shuffled deck"
  def shuffle(), do: Enum.shuffle(fresh_deck())

  @doc """
  draws a card from the deck, returns a tuple with
  the card as first element and the new deck as the
  second, will return `:empty` if there is no card in
  the deck
  """
  def draw(deck)
  def draw([card|deck]), do: {card, deck}
  def draw([]), do: :empty
end

For suites and ranks I use a sigil to create a list of atoms, as well as I do it for the ranks.

To generate the deck, I do use a comprehension to generate the tuples from the lists defined above. I do this in an attribute, because the list is generated once during compiletime this way and does not need to get regenerated every time we call fresh_deck/0.

The shuffle/0 should be pretty self-explainatory.

draw/1 just uses pattern matching to get the card and the new deck and returns it then.

If there is still stuff, that needs further explanation, feel free to ask.

josevalim

josevalim

Creator of Elixir

Every time you call deck(), you are re-using the same deck in memory. But when you shuffle the deck, the original deck is transformed, and then they become different copies. However, it is worth pointing out that each of those decks share the same cards (the tuples!). So when you shuffle, you only copy the container of the cards, the list, not the cards itself. To put it in other words, it is a shallow copy, not a deep one.

So while immutability adds the cost of transformation, it provides the benefit of transparent sharing, since it can share data structures throughout your system as they are guaranteed to never change.

josevalim

josevalim

Creator of Elixir

As I posted above, this is not actually true for Erlang/Elixir. All of the literals in your module are allocated only once in memory when the module is loaded. From there on, every time you call the function, you get the exact same element in memory:

iex(1)> defmodule Foo do
...(1)> def bar do
...(1)> [:a, :b, :c]
...(1)> end
...(1)> end
iex(2)> :erts_debug.same(Foo.bar, Foo.bar)
true
iex(3)> :erts_debug.same(Foo.bar,  [:a, :b, :c])
false

I would be surprised if other functional languages do not perform the same optimization. At least the eager ones, not sure if laziness would affect this. OTP 20 optimizes this further by not copying literals when sending messages. And OTP 21 will optimize this even further by being able to share maps keys as well, thanks to a PR from @michalmuskala.

— All posts loaded —

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
water
I’m aware of 2 such projects, are there others? git: GitHub - team-alembic/realworld: A fullstack Phoenix LiveView application with back...
New
peck
Hi everyone. My team and I have been working on a fairly modest app based around video streaming and chat, but we’ve landed a customer t...
New
bradley
I really like the adapter patterns that ecto, nebulex, waffle, etc. use and would love find something similar for a key management servic...
New
unaware8150
Hello folks! So at work, we are seeing some situations where we have to define some “fixed” strings that are used across the codebase in...
New
Alvinkariuki
How Can I Optimise Compile Time Dependencies I have been building an elixir application for about 2 years now. Many modules and files ha...
New
leoschweizer
Hi, we run different LLM models across a cluster, with each node loading a configurable subset. Model-specific queues let nodes consume ...
New

Other Trending Topics Top

mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
juhalehtonen
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New
budgie
I love Elixir. It’s one of 2 programming languages I’ve ever fallen in love with. But I don’t use it anymore. Serverless was the promis...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews