Question: Simple example of a functional process with state mutation (and error handling) #1434
-
Hi! INB4: I barely understand what I am talking about. Please forgive me if something is very stupid. I am trying to learn about functional programming currently and already implemented some logic with Either and a state delegate. After some reading I saw that version 5 will drop support for everything async and read a bit more. I tried to follow some of the examples that are available in the repository, being especially interested in the ones carrying state. This one I found TESTING.cs and I tried to adjust it (simplified) to my needs. In general I usually come across the following requirements:
I believe that chaining (?) Either is a good approach for this, however I again and again run into the problem regarding state. In the past I would use one single class (record) that contains the entire state and if a (sub)process would need to change the state, it would create a new instance of the state with a certain property now populated. So that would make the process look roughly like: await StartProcess()
.Bind(ctx => SubProcess1(ctx)
.Bind(ctx => SubProcess2(ctx); Where the context has multiple (init-only) properties that are nullable, so they can be populated by the individual sub-processes. That's stupid. First of all these nullabe properties force me to write null-checks for something that cannot happen, making the code unreadable and prevents me from covering it sensefully with tests. class State
{
public State(string initialState) { InitialState = initialState; }
public string InitialState { get; init; }
public class First : State {
public First(State state, string additionalProp) : base(state.InitialState)
{
AdditionalProp = additionalProp;
}
public string AdditionalProp { get; init; }
}
public class Second : First
{
public Second(First first) : base(first, first.AdditionalProp) { }
}
} Based on the linked example from the tests I know how to modify a state, but it didnt click so far how to achieve a chaining of different states. I reckon it is some kind of Transformer (?) I'm missing. When I naively try to change the type of the state, there is no possible SelectMany (?) OptionT<StateT<State, IO>, string> m0 = from w in Pure(123)
from q in m1
from x in StateT.get<IO, State>()
from _ in StateT.put<IO, State.First>(new(x, "my-transformed-state-prop"))
from _2 in StateT.get<IO, State.First>()
from y in m2
select $"{q + w + y}"; Is this going into the right direction? How would a process that step-by-step fills a state be handled correctly? I would be very thankful for any help and I sincerly apologize if there is an example for this exact case already and I didnt find it. Edit: I'm reading through all of the amazing blog posts currently.. But I have to admit I'm more a "learning-by-doing" guy |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 5 replies
-
@OlliMartin There's quite a bit to unpack here, so I'll try and cover what I can. If you're just learning FP then be a little wary of going all-in with monad-transformers (
static X foo(..., YourState state) =>
...
static (YourState State, X Value) foo(..., YourState state) =>
... This is what the If you're happy to go all-in, then ...
The idea is to remove the duplication of
I actually forgot about that. That was just some test-code I used whilst prototyping
One critical aspect of stacking monad-transformers is that order matters. A monad-transformer stack must be 'unstacked' when run. Usually, that would mean calling the Each layer of that unstacking creates a context for the inner layers. So, if you want state for all of your expressions, then you should have the So, instead of: OptionT<StateT<State, IO>, A> Use: StateT<State, OptionT<IO>, A> The other thing to note is that carrying types like As luck would have it, there is a sample in the Samples folder that uses
Before I write anything else I think it'd be a good thing for you to dig into that sample. It takes the ideas as far as they can go and is as close to Haskell as you're likely to get in C#. The entire application is pure (apart from |
Beta Was this translation helpful? Give feedback.
-
This is awesome, thank you for your immediate response (and apologies for not seeing the discussion tab.. facepalm). While I will finish the blog posts and then go through the card game again, this time hopefully less clueless.. Maybe I already ask this: As written above, usually I want to build some kind of functional flow, carrying a (changing) state, in the cardgame the GameState type seems to remain the same all the time. |
Beta Was this translation helpful? Give feedback.
@OlliMartin There's quite a bit to unpack here, so I'll try and cover what I can.
If you're just learning FP then be a little wary of going all-in with monad-transformers (
OptionT
,StateT
, etc.) - they're quite advanced topics... very powerful, but can be a bit of a head-scratcher to start with. My Higher Kinds series will help, but if you get stuck, know that there's a simpler route to functional programming without going quite all the way in like this. The code ends up less elegant, but is cognitively easier to start...Thi…