5

Objects tend to confuse me some. In understand the concept and use, certainly, but I feel like the functional paradigm is somewhat restricting my use of them.

At the moment, I'm creating a simple code for a game of cards. Each player might have a simple object, like this:

object player {
    name = "Steve";
    score = 0;
    hand = [/*...contains seven cards...*/];
}

and a object for a deck:

object deck {
    cards = [/*...contains the other 45 cards...*/]
}

So for dealing new cards, the simplest way would just be to mutate the this.hand and the this.cards. But... mutation.

I can think of other ways to do it, but the purely functional ways I can think of feel messy.

At what point do you draw the line between mutable and immutable data, or what am I missing?

16
  • 1
    You think of a deck of cards a "large amounts"?
    – user188153
    Commented Dec 4, 2016 at 20:54
  • Not terribly no. I guess... Actually, I think my question could be worded better, let me fix it.
    – Ucenna
    Commented Dec 4, 2016 at 20:56
  • 1
    Your pseudocode is confusing. Why this? If these are functions, why not pass them arguments? You don't need mutability here. With the small amount of data in a card game, you can easily simply generate entire new hands and decks and keep everything immutable, right?
    – Andres F.
    Commented Dec 5, 2016 at 0:17
  • 3
    Also, while I strongly approve of your trying your hand at FP, what is your goal here? Why not OOP? Are you trying to learn FP by implementing a card game? Some clarification would improve your question :)
    – Andres F.
    Commented Dec 5, 2016 at 0:19
  • 1
    This is a domain modelling problem. Entirely solvable in FP (will sit down and write something hopefully useful later today) Out of interest, what tools are you using? Or is this a purely theoretical exercise?
    – itsbruce
    Commented Dec 6, 2016 at 12:27

3 Answers 3

1

It is quite simple: if you want to go the "pure functional road", you need purely functional data structures, which means immutable data structures, which in OO languages means immutable objects.

For representing card decks, a very efficient, immutable structure is a stack, implemented as a single linked list (and not as an array!), maybe combined with a size counter. Then the typical stack operations like adding a card to the top, removing one from the top can be implemented as immutable operations in O(1). Forward iterating over all elements can be implemented in O(N). Random access to a certain card will become an O(N) operation (instead of O(1) when using an array implementation), but most meaningful card operations can be implemented without the latter, or it does not have a serious impact on the running time for most practical cases.

the purely functional ways I can think of feel messy

I don't feel so - using a stack abstraction is not necessarily more or less "messy" than using an array abstraction. The only obstacle I see is that the standard libraries of main stream OO languages like C++, C#, or Java do not provide standard implementations for something like an immutable stack "out of the box". Most functional languages probably do so today. Just google for immutable stack <your favourite language>, or immutable object tutorial <language> and you will find probably what you need.

However, if you are more used to classic OO modeling with mutable state, you can model and implement your card game using mutable classes (but then it won't very functional any more).

9
  • A description of a procedure to create a modified copy of complex structure with named and typed fields would help better.
    – Basilevs
    Commented Dec 5, 2016 at 14:36
  • @Basilevs: I did not want to dive too deep into implementation details, the Wikipedia article I linked to leads to lots of good resources and examples
    – Doc Brown
    Commented Dec 5, 2016 at 16:55
  • I only see collections there, no complex non-uniform objects. Please clarify.
    – Basilevs
    Commented Dec 5, 2016 at 16:58
  • 1
    @Basilevs: this is always straightforward - instead of mutating an object for a certain operation, let the operation internally make a shallow copy of the object, replace the attributes in that copy by the modified values, and return the new object. Unmodified collections will be just copied "by reference", and collections which need to be "modified" will be replaced by the new collection returned by the immutable equivalent operation. I am sure there are beginners examples for this approach all over the web in different programming languages, can't just find a good representative one.
    – Doc Brown
    Commented Dec 5, 2016 at 17:26
  • 1
    @Basilevs Instead of requesting things, why not write your own answer? :)
    – Andres F.
    Commented Dec 5, 2016 at 17:52
1

In FP mutation of an entity is modelled as a creation of its next state. First, the entity has one state represented by an arbitrary complex object, next moment it can have another. These states include states for all properties of entity, for example, player state may include state of his hand, world's state includes players state and deck's state.

A simple mutation, involving only one aspect of the state, is achieved by shallow copy of the previous state, replacing one of its property values.

Functional languages (should) have a designated syntax or libraries for such transformation.

Simple mutation without using designated libraries and in Javascript syntax:

function score (world) {
   return {
     deck: world.deck,
     player: {
        name: world.player.name,
        score: world.player.score + 1,
        hand: world.player.hand
    }
}

With syntax enhancements:

function score (world) {  world { player.score = world.player.score + 1 }; }

Hypothetical example with non-trivial logic, better representing real state transitions:

function win (world) {
  return {
     deck: world.deck.merge (world.player.hand),
     player: {name: world.player.name, score: world.player.score + 1, hand:[] }
}
1
  • I wonder if it is possible to bend prototype system to handle FP mutations.
    – Basilevs
    Commented Dec 5, 2016 at 18:40
-3

OO and functional programming are orthogonal concepts. So if you mix them improperly you won't get the best of both but the worst of both.

Mutating structures with functions is the worst you can do. You have side effects AND you don't know who is the responsible for consistency of the structure.

Either you encapsulate mutation into an object and let the object preserve consistency OR use immutable data structures and represent any new state with a new data structure. If you follow this then you can mix the paradigms without drawbacks.

9
  • 2
    It's demonstrably false that everything is a object, unless you tautologically define "object" as "everything expressible in my language". For example, a service is not an object. A protocol is not an object. It's also false that persistence is not solvable with a pure functional programming -- after all, real languages that do pure FP solve this problem.
    – Andres F.
    Commented Dec 5, 2016 at 22:54
  • 2
    "The other way around you have a hard day to argue". Only if you're unfamiliar with FP. It actually goes both ways; everything in OOP can be simulated by FP.
    – Andres F.
    Commented Dec 5, 2016 at 22:57
  • 1
    What distinction are you trying to make between a simulation and reality in software? The reality is bits and computations done with those bits. The simulation is bits and computations done with those bits that leads to the same result. Commented Dec 6, 2016 at 9:05
  • 3
    It's not a question about OO, it's a question about objects. If OO were the only way to deal with objects, there would be no need for the second O. We'd just talk about O or OP. Aside from that, you clearly know nothing about how state can be modelled in FP (and not a lot abut OOP, despite your choice of username)
    – itsbruce
    Commented Dec 6, 2016 at 11:56
  • 1
    I removed the obvious controversial part.
    – oopexpert
    Commented Dec 6, 2016 at 19:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.