Finq 1.0.0-alpha-01
dotnet add package Finq --version 1.0.0-alpha-01
NuGet\Install-Package Finq -Version 1.0.0-alpha-01
<PackageReference Include="Finq" Version="1.0.0-alpha-01" />
paket add Finq --version 1.0.0-alpha-01
#r "nuget: Finq, 1.0.0-alpha-01"
// Install Finq as a Cake Addin #addin nuget:?package=Finq&version=1.0.0-alpha-01&prerelease // Install Finq as a Cake Tool #tool nuget:?package=Finq&version=1.0.0-alpha-01&prerelease
Finq
⚠️ This is a work in progress. The API for Finq is not yet stable and is subject to change. Use at your own risk. It is nonetheless being published to gather feedback and allow for experimentation, both of which should help shape the final API.
Finq is a library that allows functions to be written using LINQ, as if you
never had lambda (=>
) in C#:
var f = // : Func<int, int>
from x in Funq.Arg.Int32()
select x * 2;
var g = // : Func<int, string>
from x in f
select $"{x + 2}";
Console.WriteLine(g(20)); // prints: "42"
Note that the function types resulting from using Finq are still the ones you
are familiar with; this is, the type of f
in the above example is still
Func<int, int>
, and the type of g
is Func<int, string>
. f
and g
are
still instantiations of the generic Fun<,>
and you can therefore think of Finq
as a way to write functions in a more declarative style, using LINQ.
With Finq, you can also use LINQ where a regular function is expected:
static string Join<T>(string delimiter, IEnumerable<T> xs, Func<T, string> fs) =>
string.Join(delimiter, from x in xs select fs(x));
var s = Join(Environment.NewLine,
[1, 2, 3, 4, 5],
from x in Funq.Arg.Int32()
select $"{x} => {x * Math.PI:0.0000}");
Console.WriteLine(s);
Prints:
1 => 3.1416
2 => 6.2832
3 => 9.4248
4 => 12.5664
5 => 15.7080
The LINQ in the above example is equivalent to writing x => $"{x} => {x * Math.PI:0.0000}"
, but it reads longer and can run considerably slower so the
goal of Finq is not to offer a replacement. However, it can be useful in some
situations. For example, suppose you have the following function, that takes as
an argument, a curried function:
static T Foo<T>(Func<int, Func<int, T>> f) => f(20)(2);
In C# (up to and including version 13 at the time of writing), this function
cannot be called without explicitly specifying the type of T
. Doing so:
Console.WriteLine(Foo(x => y => $"{x * 2 + y}")); // compilation error (CS0411)
results in compilation error CS0411:
The type arguments for method 'method' cannot be inferred from the usage. Try specifying the type arguments explicitly.
The C# compiler is unable to infer that the type of T
is int
from the lambda
expression. Instead, you have to write:
Console.WriteLine(Foo<string>(x => y => $"{x * 2 + y}")); // prints: 42
However, with Finq, you can write:
var f = // : Func<int, Func<int, int>>
from x in Funq.Arg.Int32()
select
from y in Funq.Arg.Int32()
select x * 2 + y;
Console.WriteLine(Foo(f)); // prints: 42
It's also impossible to explicitly specify the type of T
when the type is
anonymous, but
var f = // : Func<int, Func<int, ?>>
from x in Funq.Arg.Int32()
select
from y in Funq.Arg.Int32()
select new
{
X = x,
Y = y,
Calc = x * 2 + y
};
Console.WriteLine(Foo(f)); // prints: { X = 20, Y = 2, Calc = 42 }
Finq can also come in handy when you want to model functions as a reader functor and more. For more on this, see the “The Reader functor” blog post by Mark Seemann (aka @ploeh), and specifically the section “Raw functions”.
Finq's Funq.Arg
ship with a number of common argument types, such as Int32
,
String
, Double
, Boolean
, etc., but you can add your own to the mix by
writing an extension method for IArg
.
For ad-hoc specification of the argument type, use Funq.ArgOf<T>.Return()
:
var f = // : Func<int, int>
from x in Funq.ArgOf<int>().Return()
select x * 2;
Finq sticks to functions that take a single argument. If multiple arguments are needed then use a tuple or, preferably the parameter object pattern.
Finq also supports fallible functions via LINQ's where
clause:
var positive = // : Func<int, (bool, int)>
from x in Funq.Arg.Int32()
where x > 0
select x; // evaluated only if positive
foreach (var input in new[] { -1, 0, 1 })
{
var ok = positive.TryInvoke(input, out var result);
Console.WriteLine($"{nameof(ok)} = {ok}; {nameof(result)} = {result}");
}
Prints:
ok = False; result = 0
ok = False; result = 0
ok = True; result = 1
Using where
changes the function return type from some T
to the tuple
(bool, T)
, where the first (Boolean) element of the tuple is true
if the
second element is valid and false
otherwise. See Optuple for more background
on this pattern.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net6.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
1.0.0-alpha-01 | 75 | 6/10/2024 |