Becoming functional: adding Option of T to List when IsSome only
-
Eventually I decided to start with the functional concept and installed the LanguageExt library (see GitHub - louthy/language-ext: C# functional language extensions - a base class library for functional programming[^] ). In our code base, there is a class which is full of
bool TryGet(some parameters, out T result)
functions - I want to change them toOption TryGet(some parameters)
. But then, things start to look ugly at a different level. E.g.List result = new List();
...
foreach (IIntersectionInfo intersection in intersections)
{
Option angle = m_Geometry.TryGetAnglesForPosition(intersectionPoint, _name);
if (angle.IsNone)
{
Logger.LogWarning(Name, $"Cannot determine angle for position '{intersectionPoint}' corresponding to pixel '{pixel}'.");
}
else
{
result.Add(angle.IfNone(PointF.Empty));
}
}There are two kinds of ugliness: - I have to use the
IfNone(PointF.Empty)
clause even whenangle
is guaranteed to beSome
there. - I do not know how to get rid of theforeach
in this situation. How can the code become clean?Oh sanctissimi Wilhelmus, Theodorus, et Fredericus!
-
Eventually I decided to start with the functional concept and installed the LanguageExt library (see GitHub - louthy/language-ext: C# functional language extensions - a base class library for functional programming[^] ). In our code base, there is a class which is full of
bool TryGet(some parameters, out T result)
functions - I want to change them toOption TryGet(some parameters)
. But then, things start to look ugly at a different level. E.g.List result = new List();
...
foreach (IIntersectionInfo intersection in intersections)
{
Option angle = m_Geometry.TryGetAnglesForPosition(intersectionPoint, _name);
if (angle.IsNone)
{
Logger.LogWarning(Name, $"Cannot determine angle for position '{intersectionPoint}' corresponding to pixel '{pixel}'.");
}
else
{
result.Add(angle.IfNone(PointF.Empty));
}
}There are two kinds of ugliness: - I have to use the
IfNone(PointF.Empty)
clause even whenangle
is guaranteed to beSome
there. - I do not know how to get rid of theforeach
in this situation. How can the code become clean?Oh sanctissimi Wilhelmus, Theodorus, et Fredericus!
If you didn't want to log the failures, it could be as simple as:
m_Geometry.TryGetAnglesForPosition(intersectionPoint, _name).IfSome(result.Add);
Otherwise, something like this should work:
m_Geometry.TryGetAnglesForPosition(intersectionPoint, _name).Match(
point => result.Add(point),
() => Logger.LogWarning(Name, $"Cannot determine angle for position '{intersectionPoint}' corresponding to pixel '{pixel}'."));There's also the C# 8 property patterns:
if (m_Geometry.TryGetAnglesForPosition(intersectionPoint, _name) is { IsSome: true } angle)
{
angle.IfSome(result.Add);
}
else
{
Logger.LogWarning(Name, $"Cannot determine angle for position '{intersectionPoint}' corresponding to pixel '{pixel}'.");
}Or you could define a deconstructor as an extension method:
public static class OptionTExtensions
{
public static void Deconstruct<T>(this Option<T> option, bool isSome, T value)
{
isSome = option.IsSome;
value = isSome ? (T)option : default;
}
}
...
var (isSome, point) = m_Geometry.TryGetAnglesForPosition(intersectionPoint, _name);
if (isSome)
{
result.Add(point);
}
else
{
Logger.LogWarning(Name, $"Cannot determine angle for position '{intersectionPoint}' corresponding to pixel '{pixel}'.");
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
-
If you didn't want to log the failures, it could be as simple as:
m_Geometry.TryGetAnglesForPosition(intersectionPoint, _name).IfSome(result.Add);
Otherwise, something like this should work:
m_Geometry.TryGetAnglesForPosition(intersectionPoint, _name).Match(
point => result.Add(point),
() => Logger.LogWarning(Name, $"Cannot determine angle for position '{intersectionPoint}' corresponding to pixel '{pixel}'."));There's also the C# 8 property patterns:
if (m_Geometry.TryGetAnglesForPosition(intersectionPoint, _name) is { IsSome: true } angle)
{
angle.IfSome(result.Add);
}
else
{
Logger.LogWarning(Name, $"Cannot determine angle for position '{intersectionPoint}' corresponding to pixel '{pixel}'.");
}Or you could define a deconstructor as an extension method:
public static class OptionTExtensions
{
public static void Deconstruct<T>(this Option<T> option, bool isSome, T value)
{
isSome = option.IsSome;
value = isSome ? (T)option : default;
}
}
...
var (isSome, point) = m_Geometry.TryGetAnglesForPosition(intersectionPoint, _name);
if (isSome)
{
result.Add(point);
}
else
{
Logger.LogWarning(Name, $"Cannot determine angle for position '{intersectionPoint}' corresponding to pixel '{pixel}'.");
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
Thanks, that was the major step for this issue. Afterwards, I could figure out the
var tmp = intersections.Map(_intersection => m_Geometry.TryGet...ToList()
monstrosity abolishing theforeach(... intersections)
. Functional programming feels odd when trying it for the first time.Oh sanctissimi Wilhelmus, Theodorus, et Fredericus!