12

How do I construct a system that has all of the following:

  1. Using pure functions with immutable objects.
  2. Only pass into a function data that the function it needs, no more (i.e. no big application state object)
  3. Avoid having too many arguments to functions.
  4. Avoid having to construct new objects just for the purpose of packing and unpacking parameters to functions, simply to avoid too many parameters being passed to functions. If I'm going to pack multiple items to a function as a single object, I want that object to be the owner of that data, not something constructed temporarily

It seems to me that the State monad breaks rule #2, although it's not obvious because it's weaved in through the monad.

I have a feeling i need to use Lenses somehow, but very little is written about it for non-Functional languages.

Background

As an exercise, I'm converting one of my existing applications from an object-oriented style to a functional style. The first thing I'm trying to do is to make as much of the inner-core of the application as possible.

One thing I've heard is that how to manage "State" in a purely-functional language, and this is what I believe is done by State monads, is that logically, you call a pure function, "passing in the state of the world as it is", then when the function returns, it returns to you the state of the world as it has changed.

To illustrate, the way you can do a "hello world" in a purely functional way is kinda like, you pass in your program that state of the screen, and receive back the state of the screen with "hello world" printed on it. So technically, you're making a call to a pure function, and there are no side-effects.

Based on that, I went through my application, and: 1. First put all my application state into a single global object (GameState) 2. Second, I made GameState immutable. You can't change it. If you need a change, you have to construct a new one. I did this by adding a copy-constructor, that optionally takes one or more fields that changed. 3. To each application, I pass in the GameState as a parameter. Within the function, after it's doing what it's gonna do, it creates a new GameState and returns it.

How I have a pure functional core, and a loop on the outside that feeds that GameState into the main workflow loop of the application.

My Question:

Now, my problem is that, the GameState has about 15 different immutable objects. Many of the functions at the lowest level only operate on few of those objects, such as keeping score. So, let's say I have a function that calculates the score. Today, the GameState is passed to this function, which modifies the score by creating new GameState with a new score.

Something about that seems wrong. The function doesn't need the entirety of GameState. It just needs the Score object. So I updated it to pass in the Score, and return the Score only.

That seemed to make sense, so I went further with other functions. Some functions would require me to pass in 2, 3 or 4 parameters from the GameState, but as I used the pattern all the way the outer core of the application, I'm passing in more and more of the application state. Like, at the top of the workflow loop, I would call a method, that would call method that would call a method, etc., all the way down to where the score is calculated. That means the current score is passed along through all those layers just because a function at the very bottom is going to calculate the score.

So now I have functions with sometimes dozens of parameters. I could put those parameters into an object to lower the number of parameters, but then I would like that class to be the master location of the state application state, rather than an object that's simply constructed at the time of the call simply to avoid passing in multiple parameters, and then unpack them.

So now I'm wondering if the problem I have is that my functions are nested too deeply. This is the result of wanting to have small functions, so I refactor when a function gets to big, and split it into multiple smaller functions. But doing that produces a deeper hierarchy, and anything passed into the inner functions need to be passed in to the outer function even if the outer function isn't operating on those objects directly.

It seemed like simply passing in the GameState along the way avoided this problem. But I am back to the original problem of passing in more information to a function than the function needs.

  • 1
    I am no expert in design and specially functionnal, but since your game by nature has a state which evolve, are you sure that functional programming is a paradigm that fit in all layers of your application ? – Walfrat Jul 5 '17 at 7:25
  • Walfrat, I think if you talk to functional programming experts, you'll probably find that they would say that functional programming paradigm has solutions for managing evolving state. – Daisha Lynn Jul 5 '17 at 9:20
  • Your question seemed wider for me that only states. If it's only about managing states here is a start : see the answer and link within stackoverflow.com/questions/1020653/… – Walfrat Jul 5 '17 at 9:43
  • 2
    @DaishaLynn I don't think you should delete the question. It's been upvoted and nobody's trying to close it, so I don't think it's out of scope for this site. The lack of an answer so far may be just because it requires some relatively niche expertise. But that doesn't mean it won't be found and answered eventually. – Ben Aaronson Jul 6 '17 at 9:38
  • 2
    Managing mutable state in a complex pure functional program without substantial language assistance is a huge pain. In Haskell it's manageable because of monads, terse syntax, very good type inference but it can still be very annoying. In C# I think you'd have considerably more trouble. – Solomonoff's Secret Jul 6 '17 at 13:55
2

I'm not sure if there's a good solution. This may or not be an answer, but it's far too long for a comment. I was doing a similar thing and the following tricks have helped:

  • Split the GameState hierarchically, so you get 3-5 smaller parts instead of 15.
  • Let it implements interfaces, so your methods see only the needed parts. Don't ever cast them back as you'd lie to yourself about the real type.
  • Let also the parts implement interfaces, so you get fine control of what you pass.
  • Use parameter objects, but do it sparingly and try to turn them them into real objects with their own behavior.
  • Sometimes passing slightly more than needed is better than a lengthy parameter list.

So now I'm wondering if the problem I have is that my functions are nested too deeply.

I don't think so. Refactoring into small functions is right, but maybe you could regroup them better. Sometimes, it isn't possible, sometimes it just needs a second (or third) look at the problem.

Compare your design to the mutable one. Are there things which have got worse by the rewrite? If so, can't you make them better in the same way you did originally?

  • Someone told me to change my design so that ever function takes only one parameter so that I can use currying. I tried that one one function, so instead of calling DeleteEntity(a, b, c), now I call DeleteEntity(a)(b)(c). So that's cute, and it's supposed to make it things more composible, but i'm just not getting it yet. – Daisha Lynn Jul 9 '17 at 9:56
  • @DaishaLynn I'm using Java and there's no sweet syntactic sugar for currying, so (for me) it's not worth trying. I'm rather skeptical concerning possible usage of higher order functions in our case, but let me know if it worked for you. – maaartinus Jul 9 '17 at 12:46
2

I can't speak to C#, but in Haskell, you would end up passing the whole state around. You could do this either explicitly or with a State monad. One thing you can do to address the issue of functions receiving more information then they need is to use Has typeclasses. (If you're not familiar, Haskell typeclasses are a little like C# interfaces.) For each element E of the state, you can define a typeclass HasE that requires a function getE that returns the value of E. The State monad can then be made an instance of all these typeclasses. Then in your actual functions, instead of explicitly requiring your State monad, you require any monad that belongs to the Has typeclasses for the elements you do need; that restricts what the function can do with the monad it's using. For more info on this approach, see Michael Snoyman's post on the ReaderT design pattern.

You could probably replicate something like this in C#, depending on how you're defining the state that's being passed around. If you have something like

public class MyState
{
    public int MyInt {get; set; }
    public string MyString {get; set; }
}

you could define interfaces IHasMyInt and IHasMyString with methods GetMyInt and GetMyString respectively. The state class then looks like:

public class MyState : IHasMyInt, IHasMyString
{
    public int MyInt {get; set; }
    public string MyString {get; set; }
    public double MyDouble {get; set; }

    public int GetMyInt () 
    {
        return MyInt;
    }

    public string GetMyString ()
    {
        return MyString;
    }

    public double GetMyDouble ()
    {
        return MyDouble;
    }
}

then your methods could require IHasMyInt, IHasMyString, or the whole MyState as appropriate.

You can then use the where constraint on the function definition so that you can pass the state object, but it can only get to string and int, not double.

public static T DoSomething<T>(T state) where T : IHasMyString, IHasMyInt
{
    var s = state.GetMyString();
    var i = state.GetMyInt();
    return state;
}
  • That's interesting. So currently, where I pass call a function and pass 10 parameters by value, i pass in the "gameSt'ate" 10 times, but to 10 different parameter types, such as "IHasGameScore", "IHasGameBoard", etc. I wish there was a way to pass pass a single parameter that the function can indicate has to implement all the interfaces in that one type. I wonder if I can do it with a "generic constraint".. Let me try that. – Daisha Lynn Jul 9 '17 at 9:16
  • 1
    It worked. Here it is working: dotnetfiddle.net/cfmDbs. – Daisha Lynn Jul 9 '17 at 9:56
1

I think you would do well to learn about Redux or Elm and how they handle this question.

Basically, you have one pure function that takes the entire state and the action the user performed and returns the new state.

That function then calls other pure functions, each of which handles a particular piece of the state. Depending on the action, many of these functions may do nothing but return the original state unchanged.

To learn more, Google the Elm Architecture or Redux.js.org.

  • I don't know Elm, but I believe it's similar to Redux. In Redux, don't all the reducers get called for every state change? Sounds extremely inefficient. – Daisha Lynn Jul 9 '17 at 9:06
  • When it comes to low level optimizations, don't assume, measure. In practice it is plenty fast enough. – Daniel T. Jul 9 '17 at 12:19
  • Thanks Daniel, but it won't work for me. I've done enough development to know it doesn't make notify every component in the UI any time any data changes, regardless if control cares about the control. – Daisha Lynn Jul 10 '17 at 11:20
-2

I think what you are trying to do is to use an object oriented language in a way it should not be used, as if it was a pure functional one. It's not like OO languages were all the evil. There are advanteges of either approach so that's why we can now mix OO style with functional style and have the opportunity to make some pieces of code functional whereas other remain object oriented so that we can take advantage of all the reusability, inheritance or polimophism. Luckily we are no longer bound to either approach only so why are you trying to confine yourself to one of them?

Answering your question: no, I do not weave any particular states through the application logic but use what is appropriate for the current use case and apply the available techiques in the most appropriate way.

C# is not ready (yet) to be used as functional as you'd like it to be.

  • 3
    Not thrilled with this answer, nor the tone. I'm not abusing anything. I'm pushing the limits of C# to use it as a more Functional language. It is not an uncommon thing to do. You seem to be philosophically opposed to it, which is fine, but in that case, don't look at this question. Your comment is of no use to anyone. Move on. – Daisha Lynn Jul 9 '17 at 9:01
  • @DaishaLynn you're wrong, I'm not opposed in any way to it and in fact I use it a lot... but where it's natural and possible and not trying to turn an OO language into a functional one just becasue it's hip to do so. You don't have to agree with my answer but it doesn't change the fact that that you are not properly using your tools. – t3chb0t Jul 9 '17 at 9:07
  • I'm not doing it because it's hip to do so. C# itself is moving toward using a functional style. Anders Hejlsberg himself has indicated as such. I understand you're only interested in main-stream use of the language, and I understand why and when that's appropriate. I just don't know why someone like you is even on this thread.. How are you helping really? – Daisha Lynn Jul 9 '17 at 10:05
  • @DaishaLynn if you can't cope with answers criticizing your question or approach you probably should not be asking here questions or next time you should just add a disclaimer saying that you are only interested in answers supporting your idea 100% because you don't want to hear the truth but rather get supportive opinions. – t3chb0t Jul 9 '17 at 10:13
  • Please be a bit more cordial to one another. It's possible to give critique without disparaging language. Attempting to programming C# in a functional style is certainly not "abuse" or an edge-case. It's a common technique used by many C# developers to learn from other languages. – zumalifeguard Jul 20 '17 at 1:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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