Replies: 2 comments 1 reply
-
|
FWIW, this is the response that Chat GPT has given me (but as we all know, it can be confidently wrong. Still I will try this tomorrow): You’re asking the right questions — this is the transition point from imperative-style functional programming to composable, effect-aware functional architecture. Right now, you’re using Fin and Match, which is a great start, but you’re handling control flow with nested Match calls, which gets messy quickly. ⸻ 🔁 Your goal: You have: You want to: ⸻ ✅ Step 1: Tag errors with custom types Define distinct error types for HTTP and parsing: public abstract record IssueError(string Message) : Error(Message); //NOTE AFTER TRYING THIS SOLUTION: This DOES NOT work. It gives a compiler error: 'IssueError.Message.init': cannot override because 'Error.Message' does not have an overridable set accessor
public record HttpIssueError(string Details) : IssueError($"HTTP error: {Details}");
public record ParseIssueError(string Details) : IssueError($"Parse error: {Details}");
public record SaveIssueError(string Details) : IssueError($"Save error: {Details}");
Make your functions return them:
Task<Fin<string>> makeHttpCall(Request request); // returns HttpIssueError on fail
Fin<BusinessRecord> ParseRawString(string raw); // returns ParseIssueError on fail
Task<Fin<Unit>> SaveRecord(BusinessRecord rec); // returns SaveIssueError on fail⸻ ✅ Step 2: Compose using Bind You can use Bind and ToAsync() to compose without nesting: var result =
await makeHttpCall(request)
.MapFail(error => new HttpIssueError(error.Message)) // promote to specific error
.Bind(raw => ParseRawString(raw).MapFail(e => new ParseIssueError(e.Message)).ToAsync())
.Bind(parsed => SaveRecord(parsed).MapFail(e => new SaveIssueError(e.Message)).ToAsync());⸻ ✅ Step 3: Handle errors with pattern matching Now you handle errors in a single place: await result.Match(
Succ: _ => _logger.LogInformation("Successfully saved policy"),
Fail: error =>
{
switch (error)
{
case HttpIssueError http:
HandleHttpError(http);
break;
case ParseIssueError parse:
HandleParseError(parse);
break;
case SaveIssueError save:
HandleSaveError(save);
break;
default:
_logger.LogError($"Unknown error: {error.Message}");
break;
}
}
);⸻ ✨ Benefits ⸻ 🧭 What to read/study next ⸻ Let me know if you’d like to wrap this in a reusable workflow function or implement this using LINQ syntax with SelectMany. |
Beta Was this translation helpful? Give feedback.
-
Not a perfect example (as I had to guess your intentions): using LanguageExt;
using LanguageExt.Common;
using LanguageExt.Traits;
using static LanguageExt.Prelude;
MyRuntime rt = new(new HttpClient());
BusinessRecord dm = App<MyRuntime>.requestBusinessRecord(new Session(), new Policy()).Run(rt).ThrowIfFail();
return;
record Session;
record Policy;
static class App<RT>
where RT
: Has<Eff<RT>, HttpClient>,
Has<Eff<RT>, DbIO>
{
/// <summary>
/// Fetches business record, saves it to db and returns it. If error occurs, logs it.
/// </summary>
public static K<Eff<RT>, BusinessRecord> requestBusinessRecord(Session session, Policy policy) =>
(from str in Web<RT>.getSomeString.As()
from br in Parser<RT>.parseRawStringPolicy(str).ToEff()
from _ in Db<RT>.saveBusinessRecord(br)
select br)
| @catch(WebErrors.RequestFailedError, handleHttpError)
| @catch(ParserErrors.NameIsNullOrEmptyError, handleParseResponseError);
static K<Eff<RT>, BusinessRecord> handleParseResponseError(Error error) =>
// some handling
static K<Eff<RT>, BusinessRecord> handleHttpError(Error error) =>
// some handling
}
record MyRuntime(HttpClient Client) :
Has<Eff<MyRuntime>, HttpClient>,
Has<Eff<MyRuntime>, DbIO>
{
static K<Eff<MyRuntime>, HttpClient> Has<Eff<MyRuntime>, HttpClient>.Ask { get; } =
liftEff<MyRuntime, HttpClient>(rt => rt.Client);
static K<Eff<MyRuntime>, DbIO> Has<Eff<MyRuntime>, DbIO>.Ask { get; } =
pure<Eff<MyRuntime>, DbIO>(new EfCoreDb());
}
static class Web<RT> where RT : Has<Eff<RT>, HttpClient>
{
public static K<Eff<RT>, string> getSomeString =>
from client in Has<Eff<RT>, RT, HttpClient>.ask
from str in liftIO(async () => await client.GetStringAsync("someUrl"))
.IfFail(_ => WebErrors.RequestFailedError)
select str;
}
static class WebErrors
{
public static readonly Error RequestFailedError = Error.New("Get request failed");
}
record BusinessRecord(string Name);
static class Parser<RT>
{
public static Fin<BusinessRecord> parseRawStringPolicy(string s) =>
!string.IsNullOrEmpty(s)
? new BusinessRecord(s)
: FinFail<BusinessRecord>(ParserErrors.NameIsNullOrEmptyError);
}
static class ParserErrors
{
public static readonly Error NameIsNullOrEmptyError = Error.New("Name is null or empty");
}
interface DbIO
{
Task SaveBusinessRecordAsync(BusinessRecord record);
}
class EfCoreDb : DbIO
{
public Task SaveBusinessRecordAsync(BusinessRecord record) =>
Task.CompletedTask; // Here should be an EfCore logic
}
static class Db<RT> where RT : Has<Eff<RT>, DbIO>
{
public static K<Eff<RT>, Unit> saveBusinessRecord(BusinessRecord record) =>
from db in Has<Eff<RT>, RT, DbIO>.ask
from _ in liftIO(async () => { await db.SaveBusinessRecordAsync(record); return unit; })
.IfFail(_ => DbErrors.SaveRecordError)
select unit;
}
static class DbErrors
{
public static readonly Error SaveRecordError = Error.New("Failed to save record");
}
Notes, wiki, samples and repo's discussions.
It won't help you specifically with language-ext, as its probably aware only about v4, and v5 is not complete yet. But you could try to ask it to explain a valid code that can be found in the samples or discussions. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Hello all,
Apologies if this is a basic question—I’m still working through the documentation and blog posts, but I also need to get some code into production in the meantime.
I have a working prototype, but I suspect I’m not yet thinking in functional terms. I’m trying to improve the design.
Here’s the situation:
• I have a function that makes an HTTP call and returns a Fin:
F1: A -> Fin
• I have another function that parses the raw string into a business record:
F2: Fin -> B
I want to compose these two, but I also need to distinguish where any error comes from—either the HTTP call or the parsing—so I can update a record’s state accordingly.
How would you approach this?
So far, I’ve been using Fin and Match, but not composition. Here’s what I have so far:
My current saveParsedOrder function returns just a Task. But it also performs a side effect (a DB call). If I wanted it to return a Fin to capture success or error state explicitly, I’d end up with a third-level nested match. At that point, it feels like a code smell.
Any examples or guidance on how to structure this more cleanly—or what parts of the docs I should study to improve—would be greatly appreciated.
Best Regards
Beta Was this translation helpful? Give feedback.
All reactions