how to "Tee" (similar to "Do") for side effects
See original GitHub issueWhat is the recommended approach for doing a “tee” in LanguageExt? The concept is similar to Option.Do
but would work whether the option is Some
or None
. (A tee pipe fitting lets fluid flow through but also has a different direction… the side effect.)
My common use case is to log something before a function returns. I’ve done this by making a Tee
object extension that runs an action and returns the same object. Example usage:
return maybe
.Tee(_ => _logger.LogDebug("some message"));
Is this concept addressed already in LanguageExt? What would be a better approach?
Thank you.
Issue Analytics
- State:
- Created 4 years ago
- Comments:7 (5 by maintainers)
Top Results From Across the Web
How to Deal With Side Effects of Medicine
In other cases, you may be able to lower your dose, try a different drug, or add another one, like an anti-nausea medicine,...
Read more >Transoesophageal Echocardiography Related Complications
TEE in sitting position can cause dysphasia which is due to local effect of probe, ... Side effects of these drugs like respiratory...
Read more >Transesophageal Echocardiogram - Health Encyclopedia
A TEE can help assess and locate the abnormality, as well as determine its effect on heart blood flow.
Read more >Transesophageal Echocardiogram (TEE): Procedure Details
Talk with your provider about your medical history. Some medical conditions make a TEE too risky to perform. Tell your provider if you...
Read more >TEPEZZA Side Effects and Safety Information
Read about common TEPEZZA side effects and important safety information. ... Would fluids like coffee or alcohol make diarrhea worse?
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
@jltrem If I have an suitably complex sub-system then I usually build a bespoke monad that captures the rules of that sub-system. Whether it’s state, logging, environment (config, etc.), or control flow. My code then becomes a set of sub-systems, some that wrap others. A good example might be a compiler, where I will have sub-system monads for parsing, type-inference, code-gen, etc. that would be wrapped by a compiler monad that works with them all.
A sub-system monad shape would be defined like so:
I’ve picked the name
Subsystem
as an example, it could beTokeniser<A>
, orTypeInfer<A>
, for example.This monad takes no input and returns an
Out<A>
. TheOut<A>
will wrap anA
(the bound value of the monad) and various other stuff I want to capture. So, in your case you could wrap up aSeq<string>
for a list of output log entries. I usually wrap up anError
type as well:And so the
Out<A>
can now be defined:The
static
methods on both types are just there for friendly construction.I will then define a
Subsystem
static class that wraps up the behaviour of the monad. So, first I’ll define the success and fail methods:Notice how they’re all lambdas. The pattern matches the
Subystem<A>
delegate and so they implicitly convert to the return type.Then I’ll add the
Bind
function for the monad to theSubsystem
static class:The bind function is where you insert all the magic for your bespoke monad. This essentially runs between the lines of all operations and can do special stuff. So in this case it does error handling early-outs (like
Option
, andEither
) and exception capture (likeTry
) as well as log collection (likeWriter
).Once you have the
Bind
function then the rest of the stuff that makes the type into a functor and makes it work with LINQ is almost free:You’ll notice everything is written in terms of
Bind
- it’s usually trivial to do this once you haveBind
defined.Finally we want to add a
Log
function toSubsystem
:Note how it just creates a single item
Seq<string>
. It doesn’t need to care about how the log is built, it just needs to return a single value sequence and then the bind function does the work of joining it with other logs.We could also add a little helper function to the
Out<A>
type to make debugging a touch easier:So, now the sub-system monad is defined, we can use it. Below are two functions defined that both do some logging. The
MakeValue
function logs the value provided as well as returning it. TheAdd
function adds two values together.Not a particularly spectacular demo I know, but it should give you an idea:
The output is:
If you start working this way you’ll realise you can wrap up a lot of the scaffolding of common code patterns inside the bind function of any bespoke monad you decide to build. It also means if you decide later to add features to your monad that all existing code gets it by default (without having to thread through context objects, or use dependency injection or any of that nonsense).
Finally, by including
using static SubsystemTest.Subsystem
it’s possible to make the LINQ a bit more elegant:Super @gwintering – thanks for finding that discussion. @louthy that
Tee
extension is exactly what I’ve done. My concern was the same as you mentioned in #228: “I’ve kind of resisted it because it goes against the core concepts of functional programming (where we should avoid side-effects).”I’m curious how you two typically handle logging in your sw dev… using a Do/Tee approach? Monadic writer? Plain old imperative? If this isn’t the appropriate place to discuss, feel free to ping me on Twitter with the same handle:
@jltrem
.