Converting imperative code as a functional newbie #1165
Replies: 2 comments 5 replies
-
Hey Adam, A couple of notes first:
public interface Message
{
string Type { get; }
}
public interface HasTransactions
{
IDbTransaction BeginTransaction();
}
public interface HasQueue : HasTransactions
{
Seq<Message> ReadFromQueue();
Fin<Unit> EndConversation(Message message);
}
public interface HasMessageHandler
{
Fin<Unit> HandleMessage(Message message);
}
public static class TransactionModule
{
public static Fin<TResult> use<T, TResult>(T disposable, Func<T, Fin<TResult>> f) where T : IDisposable
{
using (var d = disposable)
{
return f(d);
}
}
public static Fin<T> transact<T>(HasTransactions service, Func<Fin<T>> f) =>
use(service.BeginTransaction(), transaction =>
{
var result = f();
if (result.IsSucc)
transaction.Commit();
else
transaction.Rollback();
return result;
});
}
public static class QueueModule
{
public static Fin<Seq<Message>> readMessagesFromQueue(HasQueue queue)
{
return FinSucc(Seq(queue.ReadFromQueue()));
}
public static Fin<Unit> endConversations(
Seq<Message> messages,
Func<Message, Fin<Unit>> processConversationEnder)
{
return
messages
.Filter(message => message.Type == "ConversationEnder")
.FoldUntil(
FinSucc(unit),
(_, message) => processConversationEnder(message),
result => result.IsFail
);
}
public static Fin<Unit> handleMessages(
Seq<Message> messages,
Func<Message, Message> convertMessage,
Func<Message, Fin<Unit>> handleMessage)
{
return
messages
.Filter(message => message.Type != "ConversationEnder")
.Map(convertMessage)
.FoldUntil(
FinSucc(unit),
(_, message) => handleMessage(message),
result => result.IsFail
);
}
}
public static class QueueService
{
// this can be anything just a pure function that does what you need
public static Message ConvertMessage(Message message) => message;
public static Fin<Unit> Run(HasQueue queue, HasMessageHandler handler) =>
from messages in QueueModule.readMessagesFromQueue(queue)
from _1 in QueueModule.endConversations(messages, queue.EndConversation)
from _2 in QueueModule.handleMessages(messages, ConvertMessage, handler.HandleMessage)
select Unit.Default;
public static Fin<Unit> RunWithTransaction(
HasTransactions dbService,
HasQueue queue,
HasMessageHandler handler) =>
from _ in TransactionModule.transact(dbService, () => Run(queue, handler))
select _;
} |
Beta Was this translation helpful? Give feedback.
-
If you're new to FP, I highly recommend Functional Programming In C# 2nd Edition by Enrico Buonanno. It took me a few readings and a LOT of experiments to get from the completely clueless newbie stage to my current mostly clueless not-quite-so newbie stage, but it's well worth it. Whilst I'm still a long way from being an expert, my code is much cleaner and more elegant than it used to be, and is much easier to test. I'm actually writing unit tests as I go along, something I always avoided before, and it's really paid off in terms of productivity. Paul Louth (the author of Language-Ext) was a technical reviewer on the book. My only criticism is that he develops his own library, rather than using Language-Ext, so you sometimes have to work a bit to translate his code into code that works here, but it's not usually that hard. Most important thing to do is keep going and don't get put off when you get stuck. |
Beta Was this translation helpful? Give feedback.
-
Hello!
I've been trying to refactor some imperativ legacy code but i'm having trouble wrapping my head around how to go at it.
I would love some input on how i should approach this code in a more functional manner:
It's so hard to translate imperative control flow when you lack functional/declarative experience!
Thank you,
/Adam
Beta Was this translation helpful? Give feedback.
All reactions