LanguageExt.Pipes 5.0.0-beta-50

This is a prerelease version of LanguageExt.Pipes.
dotnet add package LanguageExt.Pipes --version 5.0.0-beta-50
                    
NuGet\Install-Package LanguageExt.Pipes -Version 5.0.0-beta-50
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="LanguageExt.Pipes" Version="5.0.0-beta-50" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="LanguageExt.Pipes" Version="5.0.0-beta-50" />
                    
Directory.Packages.props
<PackageReference Include="LanguageExt.Pipes" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add LanguageExt.Pipes --version 5.0.0-beta-50
                    
#r "nuget: LanguageExt.Pipes, 5.0.0-beta-50"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#addin nuget:?package=LanguageExt.Pipes&version=5.0.0-beta-50&prerelease
                    
Install LanguageExt.Pipes as a Cake Addin
#tool nuget:?package=LanguageExt.Pipes&version=5.0.0-beta-50&prerelease
                    
Install LanguageExt.Pipes as a Cake Tool

LanguageExt.Pipes

If you find this feature confusing at first, and it wouldn't be surprising as it's quite a complex idea, there are some examples in the EffectsExample sample in the repo

Conventional stream programming forces you to choose only two of the following three features:

  1. Effects
  2. Streaming
  3. Composability

If you sacrifice Effects you get IEnumerable, which you can transform using composable functions in constant space, but without interleaving effects (other than of the imperative kind).

If you sacrifice Streaming you get Traverse and Sequence, which are composable and effectful, but do not return a single result until the whole list has first been processed and loaded into memory.

If you sacrifice Composability you write a tightly coupled for loops, and fire off imperative side-effects like they're going out of style. Which is streaming and effectful, but is not modular or separable.

Pipes gives you all three features: effectful, streaming, and composable programming. Pipes also provides a wide variety of stream programming abstractions which are all subsets of a single unified machinery:

The T suffix types (ProducerT, ConsumerT, PipeT, and EffectT) are the more generalist monad-transformers. They can lift any monad M you like into them, supplementing the behaviour of Pipes with the behaviour of M. The non-T suffix types (Producer, Consumer, Pipe, and Effect) only support the lifting of the Eff<RT, A> type. They're slightly easier to use, just less flexible.

All of these are connectable and you can combine them together in clever and unexpected ways because they all share the same underlying type: PipeT.

The pipes ecosystem decouples stream processing stages from each other so that you can mix and match diverse stages to produce useful streaming programs. If you are a library writer, pipes lets you package up streaming components into a reusable interface. If you are an application writer, pipes lets you connect pre-made streaming components with minimal effort to produce a highly-efficient program that streams data in constant memory.

To enforce loose coupling, components can only communicate using two commands:

  • yield: Send output data
  • awaiting: Receive input data

Pipes has four types of components built around these two commands:

  • Producer and ProducerT yield values downstream and can only do so using: Producer.yield and ProducerT.yield.
  • Consumer and ConsumerT await values from upstream and can only do so using: Consumer.awaiting and ConsumerT.awaiting.
  • Pipe and PipeT can both await and yield, using: Pipe.awaiting, PipeT.awaiting, Pipe.yield, and PipeT.yield.
  • Effect and EffectT can neither yield nor await and they model non-streaming components.

Pipes uses parametric polymorphism (i.e. generics) to overload all operations. The operator | connects Producer, Consumer, and Pipe by 'fusing' them together. Eventually they will 'fuse' together into an Effect or EffectT. This final state can be .Run(), you must fuse to an Effect or EffectT to be able to invoke any of the pipelines.

Producer, ProducerT, Consumer, ConsumerT, Pipe, Effect, and EffectT are all special cases of a single underlying type: PipeT.

You can think of it as having the following shape:

PipeT<IN, OUT, M, A>

      Upstream | Downstream
          +---------+
          |         |
     IN --►         --► OUT  -- Information flowing downstream
          |    |    |
          +----|----+
               |
               A

Pipes uses type synonyms to hide unused inputs or outputs and clean up type signatures. These type synonyms come in two flavors:

  • Concrete type synonyms that explicitly close unused inputs and outputs of the Proxy type.

  • Polymorphic type synonyms that don't explicitly close unused inputs or outputs.

The concrete type synonyms use Unit to close unused inputs and Void (the uninhabited type) to close unused outputs:

  • EffectT: explicitly closes both ends, forbidding awaiting and yield:

     EffectT<M, A> = PipeT<Unit, Void, M, A>
    
              Upstream | Downstream
    
                  +---------+
                  |         |
           Unit --►         --► Void
                  |    |    |
                  +----|----+
                       |
                       A
    
  • ProducerT: explicitly closes the upstream end, forbidding awaiting:

     ProducerT<OUT, M, A> = PipeT<Unit, OUT, M, A>
    
              Upstream | Downstream
    
                  +---------+
                  |         |
           Unit --►         --► OUT
                  |    |    |
                  +----|----+
                       |
                       A
    
  • ConsumerT: explicitly closes the downstream end, forbidding yield:

      ConsumerT<IN, M, A> = PipeT<IN, Void, M, A>
    
              Upstream | Downstream
    
                  +---------+
                  |         |
             IN --►         --► Void
                  |    |    |
                  +----|----+
                       |
                       A
    

When you compose PipeT using | all you are doing is placing them side by side and fusing them laterally. For example, when you compose a ProducerT, PipeT, and a ConsumerT, you can think of information flowing like this:

            ProducerT                     PipeT                  ConsumerT
         +-------------+             +------------+           +-------------+
         |             |             |            |           |             |
  Unit --►  readLine   --►  string --►  parseInt  --►  int  --►  writeLine  --► Void
         |      |      |             |      |     |           |      |      |
         +------|------+             +------|-----+           +------|------+
                |                           |                        |  
                A                           A                        A

Composition fuses away the intermediate interfaces, leaving behind an EffectT:

                        EffectT
        +-------------------------------------+
        |                                     |
 Unit --►   readLine | parseInt | writeLine   --► Void
        |                                     |
        +------------------|------------------+
                           |
                           A

This EffectT can be Run() which will return the composed underlying M type. Or, if it's an Effect will return the composed underlying Eff<RT, A>.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  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.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on LanguageExt.Pipes:

Package Downloads
LanguageExt.Parsec

Parser combinators library based on Haskell Parsec. This is part of the LanguageExt functional framework and requires LanguageExt.Core

LanguageExt.Sys

Extensions to language-ext framework effects system that wraps the IO behaviours from the .NET BCL

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
5.0.0-beta-50 515 4/7/2025
5.0.0-beta-49 295 2/15/2025
5.0.0-beta-48 70 2/14/2025
5.0.0-beta-47 94 2/10/2025
5.0.0-beta-46 115 2/1/2025
5.0.0-beta-45 1,118 12/27/2024
5.0.0-beta-44 81 12/25/2024
5.0.0-beta-43 70 12/23/2024
5.0.0-beta-42 77 12/19/2024
5.0.0-beta-41 75 12/19/2024
5.0.0-beta-40 94 12/17/2024
5.0.0-beta-39 262 11/27/2024
5.0.0-beta-38 114 11/18/2024
5.0.0-beta-36 98 11/6/2024
5.0.0-beta-35 68 11/6/2024
5.0.0-beta-34 146 10/28/2024