├── .gitignore ├── EnchiladaAlgorithm.png ├── FunctionFlow.Strong.CodeGen ├── FunctionFlow.Strong.CodeGen.csproj └── Program.cs ├── FunctionFlow.Strong ├── FunctionFlow.Strong.csproj ├── Generated-StrongFlowExtensions.cs ├── Generated-StrongFlows.cs ├── IHold.cs └── StrongFlow.cs ├── FunctionFlow.Test ├── FlowBuilderTests.cs └── FunctionFlow.Test.csproj ├── FunctionFlow.sln ├── FunctionFlow ├── ExpressionExtensions.cs ├── ExpressionInfo.cs ├── FlowBuilder.cs ├── FlowBuilderExtensions.cs ├── FunctionFlow.csproj ├── IFlowBuilder.cs ├── TypeExtensions.cs └── WorkItem.cs ├── ReadMe.md ├── icon.png └── icon.svg /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | 56 | # StyleCop 57 | StyleCopReport.xml 58 | 59 | # Files built by Visual Studio 60 | *_i.c 61 | *_p.c 62 | *_i.h 63 | *.ilk 64 | *.meta 65 | *.obj 66 | *.iobj 67 | *.pch 68 | *.pdb 69 | *.ipdb 70 | *.pgc 71 | *.pgd 72 | *.rsp 73 | *.sbr 74 | *.tlb 75 | *.tli 76 | *.tlh 77 | *.tmp 78 | *.tmp_proj 79 | *.log 80 | *.vspscc 81 | *.vssscc 82 | .builds 83 | *.pidb 84 | *.svclog 85 | *.scc 86 | 87 | # Chutzpah Test files 88 | _Chutzpah* 89 | 90 | # Visual C++ cache files 91 | ipch/ 92 | *.aps 93 | *.ncb 94 | *.opendb 95 | *.opensdf 96 | *.sdf 97 | *.cachefile 98 | *.VC.db 99 | *.VC.VC.opendb 100 | 101 | # Visual Studio profiler 102 | *.psess 103 | *.vsp 104 | *.vspx 105 | *.sap 106 | 107 | # Visual Studio Trace Files 108 | *.e2e 109 | 110 | # TFS 2012 Local Workspace 111 | $tf/ 112 | 113 | # Guidance Automation Toolkit 114 | *.gpState 115 | 116 | # ReSharper is a .NET coding add-in 117 | _ReSharper*/ 118 | *.[Rr]e[Ss]harper 119 | *.DotSettings.user 120 | 121 | # JustCode is a .NET coding add-in 122 | .JustCode 123 | 124 | # TeamCity is a build add-in 125 | _TeamCity* 126 | 127 | # DotCover is a Code Coverage Tool 128 | *.dotCover 129 | 130 | # AxoCover is a Code Coverage Tool 131 | .axoCover/* 132 | !.axoCover/settings.json 133 | 134 | # Visual Studio code coverage results 135 | *.coverage 136 | *.coveragexml 137 | 138 | # NCrunch 139 | _NCrunch_* 140 | .*crunch*.local.xml 141 | nCrunchTemp_* 142 | 143 | # MightyMoose 144 | *.mm.* 145 | AutoTest.Net/ 146 | 147 | # Web workbench (sass) 148 | .sass-cache/ 149 | 150 | # Installshield output folder 151 | [Ee]xpress/ 152 | 153 | # DocProject is a documentation generator add-in 154 | DocProject/buildhelp/ 155 | DocProject/Help/*.HxT 156 | DocProject/Help/*.HxC 157 | DocProject/Help/*.hhc 158 | DocProject/Help/*.hhk 159 | DocProject/Help/*.hhp 160 | DocProject/Help/Html2 161 | DocProject/Help/html 162 | 163 | # Click-Once directory 164 | publish/ 165 | 166 | # Publish Web Output 167 | *.[Pp]ublish.xml 168 | *.azurePubxml 169 | # Note: Comment the next line if you want to checkin your web deploy settings, 170 | # but database connection strings (with potential passwords) will be unencrypted 171 | *.pubxml 172 | *.publishproj 173 | 174 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 175 | # checkin your Azure Web App publish settings, but sensitive information contained 176 | # in these scripts will be unencrypted 177 | PublishScripts/ 178 | 179 | # NuGet Packages 180 | *.nupkg 181 | # The packages folder can be ignored because of Package Restore 182 | **/[Pp]ackages/* 183 | # except build/, which is used as an MSBuild target. 184 | !**/[Pp]ackages/build/ 185 | # Uncomment if necessary however generally it will be regenerated when needed 186 | #!**/[Pp]ackages/repositories.config 187 | # NuGet v3's project.json files produces more ignorable files 188 | *.nuget.props 189 | *.nuget.targets 190 | 191 | # Microsoft Azure Build Output 192 | csx/ 193 | *.build.csdef 194 | 195 | # Microsoft Azure Emulator 196 | ecf/ 197 | rcf/ 198 | 199 | # Windows Store app package directories and files 200 | AppPackages/ 201 | BundleArtifacts/ 202 | Package.StoreAssociation.xml 203 | _pkginfo.txt 204 | *.appx 205 | 206 | # Visual Studio cache files 207 | # files ending in .cache can be ignored 208 | *.[Cc]ache 209 | # but keep track of directories ending in .cache 210 | !*.[Cc]ache/ 211 | 212 | # Others 213 | ClientBin/ 214 | ~$* 215 | *~ 216 | *.dbmdl 217 | *.dbproj.schemaview 218 | *.jfm 219 | *.pfx 220 | *.publishsettings 221 | orleans.codegen.cs 222 | 223 | # Including strong name files can present a security risk 224 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 225 | #*.snk 226 | 227 | # Since there are multiple workflows, uncomment next line to ignore bower_components 228 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 229 | #bower_components/ 230 | 231 | # RIA/Silverlight projects 232 | Generated_Code/ 233 | 234 | # Backup & report files from converting an old project file 235 | # to a newer Visual Studio version. Backup files are not needed, 236 | # because we have git ;-) 237 | _UpgradeReport_Files/ 238 | Backup*/ 239 | UpgradeLog*.XML 240 | UpgradeLog*.htm 241 | ServiceFabricBackup/ 242 | *.rptproj.bak 243 | 244 | # SQL Server files 245 | *.mdf 246 | *.ldf 247 | *.ndf 248 | 249 | # Business Intelligence projects 250 | *.rdl.data 251 | *.bim.layout 252 | *.bim_*.settings 253 | *.rptproj.rsuser 254 | 255 | # Microsoft Fakes 256 | FakesAssemblies/ 257 | 258 | # GhostDoc plugin setting file 259 | *.GhostDoc.xml 260 | 261 | # Node.js Tools for Visual Studio 262 | .ntvs_analysis.dat 263 | node_modules/ 264 | 265 | # Visual Studio 6 build log 266 | *.plg 267 | 268 | # Visual Studio 6 workspace options file 269 | *.opt 270 | 271 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 272 | *.vbw 273 | 274 | # Visual Studio LightSwitch build output 275 | **/*.HTMLClient/GeneratedArtifacts 276 | **/*.DesktopClient/GeneratedArtifacts 277 | **/*.DesktopClient/ModelManifest.xml 278 | **/*.Server/GeneratedArtifacts 279 | **/*.Server/ModelManifest.xml 280 | _Pvt_Extensions 281 | 282 | # Paket dependency manager 283 | .paket/paket.exe 284 | paket-files/ 285 | 286 | # FAKE - F# Make 287 | .fake/ 288 | 289 | # JetBrains Rider 290 | .idea/ 291 | *.sln.iml 292 | 293 | # CodeRush 294 | .cr/ 295 | 296 | # Python Tools for Visual Studio (PTVS) 297 | __pycache__/ 298 | *.pyc 299 | 300 | # Cake - Uncomment if you are using it 301 | # tools/** 302 | # !tools/packages.config 303 | 304 | # Tabs Studio 305 | *.tss 306 | 307 | # Telerik's JustMock configuration file 308 | *.jmconfig 309 | 310 | # BizTalk build output 311 | *.btp.cs 312 | *.btm.cs 313 | *.odx.cs 314 | *.xsd.cs 315 | 316 | # OpenCover UI analysis results 317 | OpenCover/ 318 | 319 | # Azure Stream Analytics local run output 320 | ASALocalRun/ 321 | 322 | # MSBuild Binary and Structured Log 323 | *.binlog 324 | 325 | # NVidia Nsight GPU debugger configuration file 326 | *.nvuser 327 | 328 | # MFractors (Xamarin productivity tool) working folder 329 | .mfractor/ 330 | -------------------------------------------------------------------------------- /EnchiladaAlgorithm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Prototypist1/FunctionFlow/b85de0e571a2e92c5b4773f4fb5da6b6c09f8abf/EnchiladaAlgorithm.png -------------------------------------------------------------------------------- /FunctionFlow.Strong.CodeGen/FunctionFlow.Strong.CodeGen.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | Prototypist.FunctionFlow.Strong.CodeGen 7 | FunctionFlow.Strong.CodeGen 8 | 9 | 10 | 11 | 7.3 12 | 13 | 14 | 15 | 7.3 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /FunctionFlow.Strong.CodeGen/Program.cs: -------------------------------------------------------------------------------- 1 | using Prototypist.FunctionFlow.Strong; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Prototypist.FunctionFlow.Strong.CodeGen 10 | { 11 | 12 | class Program 13 | { 14 | private const string _FunctionFlow = nameof(Prototypist.FunctionFlow); 15 | private const string _FlowBuilderProp = nameof(Prototypist.FunctionFlow.FlowBuilder); 16 | private const string _StrongFlow = nameof(Prototypist.FunctionFlow.Strong.StrongFlow); 17 | private const string _StrongFlowBase = nameof(Prototypist.FunctionFlow.Strong.StrongFlowBase); 18 | private const string _FlowBuilderType = nameof(Prototypist.FunctionFlow.FlowBuilder); 19 | private const string _Set = nameof(Prototypist.FunctionFlow.FlowBuilder.SetConstant); 20 | private const string _AddStep = nameof(Prototypist.FunctionFlow.FlowBuilder.AddStep); 21 | private const string _AddStepPacked = nameof(Prototypist.FunctionFlow.FlowBuilder.AddStepPacked); 22 | private const string _IHold = nameof(Prototypist.FunctionFlow.Strong.IHold); 23 | private const string _IStrongFlow = nameof(Prototypist.FunctionFlow.Strong.IStrongFlow); 24 | private static readonly string _IHack = Name(typeof(Prototypist.FunctionFlow.Strong.IHack<,>)); 25 | private const int MaxOutputSize = 2; 26 | private const int MaxClassSize = 12; 27 | 28 | public class Frog { 29 | public int FlysEaten; 30 | 31 | public Frog(int flysEaten) 32 | { 33 | FlysEaten = flysEaten; 34 | } 35 | } 36 | public class Cat { 37 | public Frog Frog; 38 | 39 | public Cat(Frog frog) 40 | { 41 | Frog = frog ?? throw new ArgumentNullException(nameof(frog)); 42 | } 43 | } 44 | public class Pig { } 45 | 46 | 47 | static void Main(string[] args) 48 | { 49 | WriteHolders(); 50 | WriteExtensions(); 51 | } 52 | 53 | private static void WriteExtensions() 54 | { 55 | var extensions = ""; 56 | 57 | extensions += Environment.NewLine; 58 | extensions += $"#region {nameof(Add)}" + Environment.NewLine; 59 | 60 | for (int classSize = 0; classSize < MaxClassSize; classSize++) 61 | { 62 | for (int inputSize = 0; inputSize <= classSize; inputSize++) 63 | { 64 | extensions += Add(classSize, inputSize); 65 | } 66 | } 67 | 68 | extensions += Environment.NewLine; 69 | extensions += "#endregion" + Environment.NewLine; 70 | extensions += Environment.NewLine; 71 | extensions += $"#region {nameof(Update)}" + Environment.NewLine; 72 | 73 | for (int classSize = 1; classSize <= MaxClassSize; classSize++) 74 | { 75 | for (int inputSize = 0; inputSize <= classSize; inputSize++) 76 | { 77 | extensions += Update(classSize, inputSize); 78 | } 79 | } 80 | 81 | extensions += Environment.NewLine; 82 | extensions += "#endregion" + Environment.NewLine; 83 | extensions += Environment.NewLine; 84 | extensions += $"#region {nameof(PackedAdd)}" + Environment.NewLine; 85 | 86 | for (int classSize = 0; classSize < MaxClassSize; classSize++) 87 | { 88 | for (int inputSize = 0; inputSize <= classSize; inputSize++) 89 | { 90 | for (int outputSize = 2; outputSize + classSize <= MaxClassSize && outputSize <= MaxOutputSize; outputSize++) 91 | { 92 | extensions += PackedAdd(classSize, inputSize, outputSize); 93 | } 94 | } 95 | } 96 | 97 | extensions += Environment.NewLine; 98 | extensions += "#endregion" + Environment.NewLine; 99 | extensions += Environment.NewLine; 100 | extensions += $"#region {nameof(PackedUpdate)}" + Environment.NewLine; 101 | 102 | for (int classSize = 2; classSize <= MaxClassSize; classSize++) 103 | { 104 | for (int inputSize = 0; inputSize <= classSize; inputSize++) 105 | { 106 | for (int outputSize = 2; classSize <= MaxClassSize && outputSize <= MaxOutputSize; outputSize++) 107 | { 108 | extensions += PackedUpdate(classSize, inputSize, outputSize); 109 | } 110 | } 111 | } 112 | 113 | extensions += "#endregion" + Environment.NewLine; 114 | 115 | var extensionsText = WrapNameSpace(WrapExtensions(extensions)); 116 | File.WriteAllText($"../../../../{_FunctionFlow}.Strong/Generated-{_StrongFlow}Extensions.cs", extensionsText); 117 | } 118 | 119 | private static void WriteHolders() 120 | { 121 | var holders = ""; 122 | 123 | holders += Environment.NewLine; 124 | holders += $"#region {nameof(BaseClass)}" + Environment.NewLine; 125 | 126 | for (int classSize = 1; classSize <= MaxClassSize; classSize++) 127 | { 128 | holders += BaseClass(classSize); 129 | } 130 | 131 | holders += Environment.NewLine; 132 | holders += "#endregion" + Environment.NewLine; 133 | holders += Environment.NewLine; 134 | holders += $"#region {nameof(Class)}" + Environment.NewLine; 135 | 136 | for (int classSize = 1; classSize <= MaxClassSize; classSize++) 137 | { 138 | holders += Class(classSize); 139 | } 140 | 141 | holders += Environment.NewLine; 142 | holders += "#endregion" + Environment.NewLine; 143 | holders += Environment.NewLine; 144 | holders += $"#region {nameof(Interface)}" + Environment.NewLine; 145 | 146 | for (int classSize = 1; classSize <= MaxClassSize; classSize++) 147 | { 148 | holders += Interface(classSize); 149 | } 150 | 151 | holders += "#endregion" + Environment.NewLine; 152 | 153 | var holdersText = WrapNameSpace(holders); 154 | File.WriteAllText($"../../../../{_FunctionFlow}.Strong/Generated-{_StrongFlow}s.cs", holdersText); 155 | } 156 | 157 | private static string WrapExtensions(string inner) 158 | { 159 | return $@" 160 | public static class {_StrongFlow}Extensions 161 | {{ 162 | {inner} 163 | }}"; 164 | } 165 | 166 | private static string WrapNameSpace(string inner) 167 | { 168 | return $@" 169 | using System; 170 | using Prototypist.{_FunctionFlow}; 171 | 172 | namespace Prototypist.{_FunctionFlow}.Strong 173 | {{ 174 | {inner} 175 | }}"; 176 | } 177 | 178 | static string Interface(int n) { 179 | return $@" 180 | public interface {_IStrongFlow}{(n > 0 ? "<" : "")}{GenericTs(n)}{(n > 0 ? ">" : "")} : {_IHack}<{_StrongFlow}{(n > 0 ? "<" : "")}{GenericTs(n)}{(n > 0 ? ">" : "")}, {_StrongFlow}{(n > 0 ? "<" : "")}{GenericTs(n)}{(n > 0 ? ">" : "")}> 181 | {{ 182 | }} 183 | "; 184 | } 185 | 186 | static string BaseClass(int n) 187 | { 188 | var typeName = $"{_StrongFlowBase}<{GenericTs(n)}>"; 189 | 190 | return $@" 191 | public class {typeName} : {_StrongFlowBase}{(n - 1 > 0 ? "<" : "")}{GenericTs(n - 1)}{(n - 1 > 0 ? ">" : "")}, {_IHold} 192 | {{ 193 | public {_StrongFlowBase}({_StrongFlowBase} backing) : base(backing) 194 | {{ 195 | }} 196 | 197 | protected {_StrongFlowBase}({_FlowBuilderType} flowBuilder) : base(flowBuilder) 198 | {{ 199 | }} 200 | }} 201 | "; 202 | } 203 | 204 | static string Class(int n) { 205 | var baseName = $"{_StrongFlowBase}{(n > 0 ? "<" : "")}{GenericTs(n)}{(n > 0 ? ">" : "")}"; 206 | var typeName = $"{_StrongFlow}{(n > 0 ? "<" : "")}{GenericTs(n)}{(n > 0 ? ">" : "")}"; 207 | 208 | return $@" 209 | public class {typeName} : {baseName}, {_IStrongFlow}<{GenericTs(n)}> 210 | {{ 211 | public {_StrongFlow}({_StrongFlowBase} backing): base(backing) {{ 212 | }} 213 | public {_StrongFlow}({Parameters(n)}): base(new {_FlowBuilderType}()) {{ 214 | {Set(n)} 215 | }} 216 | }} 217 | "; 218 | 219 | } 220 | 221 | 222 | public static string Add(int n, int funcIn) { 223 | return $@" 224 | public static {_IStrongFlow}<{GenericTs(n)}{(n == 0 ? "" : ", ")}TOut> {nameof(Add)}(this {_IHack}<{_StrongFlow}{(n != 0 ? "<" : "")}{GenericTs(n)}{(n != 0 ? ">" : "")}, T{_StrongFlow}> self, Func<{GenericTIns(funcIn) + (funcIn == 0? "":", ") }TOut> func) 225 | where T{_StrongFlow} : {_StrongFlow}{(n != 0 ? "<":"")}{GenericTs(n)}{(n != 0 ? ">" : "")} {Holds(funcIn)} 226 | {{ 227 | self.{_AddStep}(func); 228 | return new {_StrongFlow}<{GenericTs(n)}{(n == 0 ? "" : ", ")}TOut>(({_StrongFlowBase})self); 229 | }} 230 | "; 231 | } 232 | 233 | public static string Update(int n, int funcIn) 234 | { 235 | return $@" 236 | public static {_IStrongFlow}<{GenericTs(n)}> {nameof(Update)}(this {_IHack}<{_StrongFlow}{(n != 0 ? "<" : "")}{GenericTs(n)}{(n != 0 ? ">" : "")}, T{_StrongFlow}> self, Func<{GenericTIns(funcIn) + (funcIn == 0 ? "" : ", ") }TOut> func) 237 | where T{_StrongFlow} : {_StrongFlow}{(n != 0 ? "<" : "")}{GenericTs(n)}{(n != 0 ? ">" : "")} {Holds(funcIn)}, {_IHold} 238 | {{ 239 | self.{_AddStep}(func); 240 | return new {_StrongFlow}<{GenericTs(n)}>(({_StrongFlowBase})self); 241 | }} 242 | "; 243 | } 244 | 245 | public static string PackedAdd(int n, int funcIn, int funcOut) 246 | { 247 | return $@" 248 | public static {_IStrongFlow}<{GenericTs(n)}{(n == 0 ? "" : ", ")}{GenericTOuts(funcOut)}> {nameof(PackedAdd)}(this {_IHack}<{_StrongFlow}{(n != 0 ? "<" : "")}{GenericTs(n)}{(n != 0 ? ">" : "")}, T{_StrongFlow}> self, Func<{GenericTIns(funcIn)}{(funcIn == 0 ? "" : ", ")}({GenericTOuts(funcOut)})> func) 249 | where T{_StrongFlow} : {_StrongFlow}{(n != 0 ? "<" : "")}{GenericTs(n)}{(n != 0 ? ">" : "")} {Holds(funcIn)} 250 | {{ 251 | self.{_AddStepPacked}<{GenericTOuts(funcOut)}>(func); 252 | return new {_StrongFlow}<{GenericTs(n)}{(n == 0 ? "" : ", ")}{GenericTOuts(funcOut)}>(({_StrongFlowBase})self); 253 | }} 254 | "; 255 | } 256 | 257 | public static string PackedUpdate(int n, int funcIn, int funcOut) 258 | { 259 | return $@" 260 | public static {_IStrongFlow}<{GenericTs(n)}> {nameof(PackedUpdate)}(this {_IHack}<{_StrongFlow}{(n != 0 ? "<" : "")}{GenericTs(n)}{(n != 0 ? ">" : "")}, T{_StrongFlow}> self, Func<{GenericTIns(funcIn)}{(funcIn == 0 ? "" : ", ")}({GenericTOuts(funcOut)})> func) 261 | where T{_StrongFlow} : {_StrongFlow}{(n != 0 ? "<" : "")}{GenericTs(n)}{(n != 0 ? ">" : "")} {Holds(funcIn)} {HoldsOut(funcOut)} 262 | {{ 263 | self.{_AddStepPacked}<{GenericTOuts(funcOut)}>(func); 264 | return new {_StrongFlow}<{GenericTs(n)}>(({_StrongFlowBase})self); 265 | }} 266 | "; 267 | } 268 | 269 | private static string Name(Type type) 270 | { 271 | return type.Name.Substring(0, type.Name.LastIndexOf('`')); 272 | } 273 | 274 | private static string Set(int n) 275 | { 276 | var res = ""; 277 | for (int i = 0; i < n; i++) 278 | { 279 | res += $" {_Set}(t{i + 1});" + (i != n-1?Environment.NewLine:""); 280 | } 281 | return res; 282 | } 283 | 284 | static string Holds(int n) 285 | { 286 | if (n == 0) { return ""; } 287 | var res = ""; 288 | 289 | for (int i = 0; i < n; i++) 290 | { 291 | res += $", {_IHold}"; 292 | } 293 | return res; 294 | } 295 | 296 | static string HoldsOut(int n) 297 | { 298 | if (n == 0) { return ""; } 299 | var res = ""; 300 | 301 | for (int i = 0; i < n; i++) 302 | { 303 | res += $", {_IHold}"; 304 | } 305 | return res; 306 | } 307 | 308 | static string GenericTOuts(int n) 309 | { 310 | var res = ""; 311 | 312 | for (int i = 0; i < n; i++) 313 | { 314 | res += $"TOut{i + 1}"; 315 | if (i != n - 1) 316 | { 317 | res += ", "; 318 | } 319 | } 320 | return res; 321 | } 322 | 323 | static string GenericTIns(int n) 324 | { 325 | var res = ""; 326 | 327 | for (int i = 0; i < n; i++) 328 | { 329 | res += $"TIn{i+1}"; 330 | if (i != n -1) { 331 | res += ", "; 332 | } 333 | } 334 | return res; 335 | } 336 | 337 | static string GenericTs(int n) { 338 | var res = ""; 339 | 340 | for (int i = 0; i < n; i++) 341 | { 342 | res += $"T{i + 1}"; 343 | if (i != n - 1) 344 | { 345 | res += ", "; 346 | } 347 | } 348 | return res; 349 | } 350 | 351 | static string Parameters(int n) 352 | { 353 | var res = ""; 354 | 355 | for (int i = 0; i < n; i++) 356 | { 357 | res += $"T{i + 1} t{i+1}"; 358 | if (i != n - 1) 359 | { 360 | res += ", "; 361 | } 362 | } 363 | return res; 364 | } 365 | 366 | static string BaseInput(int n) 367 | { 368 | var res = ""; 369 | 370 | for (int i = 0; i < n; i++) 371 | { 372 | res += $"t{i + 1}"; 373 | if (i != n - 1) 374 | { 375 | res += ", "; 376 | } 377 | } 378 | return res; 379 | } 380 | } 381 | } 382 | 383 | -------------------------------------------------------------------------------- /FunctionFlow.Strong/FunctionFlow.Strong.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | Prototypist.FunctionFlow.Strong 6 | False 7 | Prototypist 8 | Prototypist 9 | "Strongly typed" extensions for function graph 10 | false 11 | Prototypist.pfx 12 | false 13 | 1.0.0.1 14 | 1.0.0.1 15 | 1.0.0.1 16 | FunctionFlow.Strong 17 | git 18 | 19 | https://github.com/Prototypist1/FunctionFlow 20 | https://github.com/Prototypist1/FunctionFlow 21 | 2018 Colin Wielga 22 | FunctionFlow.Strong 23 | Prototypist.FunctionFlow.Strong 24 | - 25 | False 26 | MIT 27 | icon.png 28 | 29 | 30 | 31 | 7.3 32 | 33 | 34 | 35 | 7.3 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | True 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /FunctionFlow.Strong/Generated-StrongFlows.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using Prototypist.FunctionFlow; 4 | 5 | namespace Prototypist.FunctionFlow.Strong 6 | { 7 | 8 | #region BaseClass 9 | 10 | public class StrongFlowBase : StrongFlowBase, IHold 11 | { 12 | public StrongFlowBase(StrongFlowBase backing) : base(backing) 13 | { 14 | } 15 | 16 | protected StrongFlowBase(FlowBuilder flowBuilder) : base(flowBuilder) 17 | { 18 | } 19 | } 20 | 21 | public class StrongFlowBase : StrongFlowBase, IHold 22 | { 23 | public StrongFlowBase(StrongFlowBase backing) : base(backing) 24 | { 25 | } 26 | 27 | protected StrongFlowBase(FlowBuilder flowBuilder) : base(flowBuilder) 28 | { 29 | } 30 | } 31 | 32 | public class StrongFlowBase : StrongFlowBase, IHold 33 | { 34 | public StrongFlowBase(StrongFlowBase backing) : base(backing) 35 | { 36 | } 37 | 38 | protected StrongFlowBase(FlowBuilder flowBuilder) : base(flowBuilder) 39 | { 40 | } 41 | } 42 | 43 | public class StrongFlowBase : StrongFlowBase, IHold 44 | { 45 | public StrongFlowBase(StrongFlowBase backing) : base(backing) 46 | { 47 | } 48 | 49 | protected StrongFlowBase(FlowBuilder flowBuilder) : base(flowBuilder) 50 | { 51 | } 52 | } 53 | 54 | public class StrongFlowBase : StrongFlowBase, IHold 55 | { 56 | public StrongFlowBase(StrongFlowBase backing) : base(backing) 57 | { 58 | } 59 | 60 | protected StrongFlowBase(FlowBuilder flowBuilder) : base(flowBuilder) 61 | { 62 | } 63 | } 64 | 65 | public class StrongFlowBase : StrongFlowBase, IHold 66 | { 67 | public StrongFlowBase(StrongFlowBase backing) : base(backing) 68 | { 69 | } 70 | 71 | protected StrongFlowBase(FlowBuilder flowBuilder) : base(flowBuilder) 72 | { 73 | } 74 | } 75 | 76 | public class StrongFlowBase : StrongFlowBase, IHold 77 | { 78 | public StrongFlowBase(StrongFlowBase backing) : base(backing) 79 | { 80 | } 81 | 82 | protected StrongFlowBase(FlowBuilder flowBuilder) : base(flowBuilder) 83 | { 84 | } 85 | } 86 | 87 | public class StrongFlowBase : StrongFlowBase, IHold 88 | { 89 | public StrongFlowBase(StrongFlowBase backing) : base(backing) 90 | { 91 | } 92 | 93 | protected StrongFlowBase(FlowBuilder flowBuilder) : base(flowBuilder) 94 | { 95 | } 96 | } 97 | 98 | public class StrongFlowBase : StrongFlowBase, IHold 99 | { 100 | public StrongFlowBase(StrongFlowBase backing) : base(backing) 101 | { 102 | } 103 | 104 | protected StrongFlowBase(FlowBuilder flowBuilder) : base(flowBuilder) 105 | { 106 | } 107 | } 108 | 109 | public class StrongFlowBase : StrongFlowBase, IHold 110 | { 111 | public StrongFlowBase(StrongFlowBase backing) : base(backing) 112 | { 113 | } 114 | 115 | protected StrongFlowBase(FlowBuilder flowBuilder) : base(flowBuilder) 116 | { 117 | } 118 | } 119 | 120 | public class StrongFlowBase : StrongFlowBase, IHold 121 | { 122 | public StrongFlowBase(StrongFlowBase backing) : base(backing) 123 | { 124 | } 125 | 126 | protected StrongFlowBase(FlowBuilder flowBuilder) : base(flowBuilder) 127 | { 128 | } 129 | } 130 | 131 | public class StrongFlowBase : StrongFlowBase, IHold 132 | { 133 | public StrongFlowBase(StrongFlowBase backing) : base(backing) 134 | { 135 | } 136 | 137 | protected StrongFlowBase(FlowBuilder flowBuilder) : base(flowBuilder) 138 | { 139 | } 140 | } 141 | #endregion 142 | 143 | #region Class 144 | 145 | public class StrongFlow : StrongFlowBase, IStrongFlow 146 | { 147 | public StrongFlow(StrongFlowBase backing): base(backing) { 148 | } 149 | public StrongFlow(T1 t1): base(new FlowBuilder()) { 150 | SetConstant(t1); 151 | } 152 | } 153 | 154 | public class StrongFlow : StrongFlowBase, IStrongFlow 155 | { 156 | public StrongFlow(StrongFlowBase backing): base(backing) { 157 | } 158 | public StrongFlow(T1 t1, T2 t2): base(new FlowBuilder()) { 159 | SetConstant(t1); 160 | SetConstant(t2); 161 | } 162 | } 163 | 164 | public class StrongFlow : StrongFlowBase, IStrongFlow 165 | { 166 | public StrongFlow(StrongFlowBase backing): base(backing) { 167 | } 168 | public StrongFlow(T1 t1, T2 t2, T3 t3): base(new FlowBuilder()) { 169 | SetConstant(t1); 170 | SetConstant(t2); 171 | SetConstant(t3); 172 | } 173 | } 174 | 175 | public class StrongFlow : StrongFlowBase, IStrongFlow 176 | { 177 | public StrongFlow(StrongFlowBase backing): base(backing) { 178 | } 179 | public StrongFlow(T1 t1, T2 t2, T3 t3, T4 t4): base(new FlowBuilder()) { 180 | SetConstant(t1); 181 | SetConstant(t2); 182 | SetConstant(t3); 183 | SetConstant(t4); 184 | } 185 | } 186 | 187 | public class StrongFlow : StrongFlowBase, IStrongFlow 188 | { 189 | public StrongFlow(StrongFlowBase backing): base(backing) { 190 | } 191 | public StrongFlow(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5): base(new FlowBuilder()) { 192 | SetConstant(t1); 193 | SetConstant(t2); 194 | SetConstant(t3); 195 | SetConstant(t4); 196 | SetConstant(t5); 197 | } 198 | } 199 | 200 | public class StrongFlow : StrongFlowBase, IStrongFlow 201 | { 202 | public StrongFlow(StrongFlowBase backing): base(backing) { 203 | } 204 | public StrongFlow(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6): base(new FlowBuilder()) { 205 | SetConstant(t1); 206 | SetConstant(t2); 207 | SetConstant(t3); 208 | SetConstant(t4); 209 | SetConstant(t5); 210 | SetConstant(t6); 211 | } 212 | } 213 | 214 | public class StrongFlow : StrongFlowBase, IStrongFlow 215 | { 216 | public StrongFlow(StrongFlowBase backing): base(backing) { 217 | } 218 | public StrongFlow(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7): base(new FlowBuilder()) { 219 | SetConstant(t1); 220 | SetConstant(t2); 221 | SetConstant(t3); 222 | SetConstant(t4); 223 | SetConstant(t5); 224 | SetConstant(t6); 225 | SetConstant(t7); 226 | } 227 | } 228 | 229 | public class StrongFlow : StrongFlowBase, IStrongFlow 230 | { 231 | public StrongFlow(StrongFlowBase backing): base(backing) { 232 | } 233 | public StrongFlow(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8): base(new FlowBuilder()) { 234 | SetConstant(t1); 235 | SetConstant(t2); 236 | SetConstant(t3); 237 | SetConstant(t4); 238 | SetConstant(t5); 239 | SetConstant(t6); 240 | SetConstant(t7); 241 | SetConstant(t8); 242 | } 243 | } 244 | 245 | public class StrongFlow : StrongFlowBase, IStrongFlow 246 | { 247 | public StrongFlow(StrongFlowBase backing): base(backing) { 248 | } 249 | public StrongFlow(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9): base(new FlowBuilder()) { 250 | SetConstant(t1); 251 | SetConstant(t2); 252 | SetConstant(t3); 253 | SetConstant(t4); 254 | SetConstant(t5); 255 | SetConstant(t6); 256 | SetConstant(t7); 257 | SetConstant(t8); 258 | SetConstant(t9); 259 | } 260 | } 261 | 262 | public class StrongFlow : StrongFlowBase, IStrongFlow 263 | { 264 | public StrongFlow(StrongFlowBase backing): base(backing) { 265 | } 266 | public StrongFlow(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10): base(new FlowBuilder()) { 267 | SetConstant(t1); 268 | SetConstant(t2); 269 | SetConstant(t3); 270 | SetConstant(t4); 271 | SetConstant(t5); 272 | SetConstant(t6); 273 | SetConstant(t7); 274 | SetConstant(t8); 275 | SetConstant(t9); 276 | SetConstant(t10); 277 | } 278 | } 279 | 280 | public class StrongFlow : StrongFlowBase, IStrongFlow 281 | { 282 | public StrongFlow(StrongFlowBase backing): base(backing) { 283 | } 284 | public StrongFlow(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11): base(new FlowBuilder()) { 285 | SetConstant(t1); 286 | SetConstant(t2); 287 | SetConstant(t3); 288 | SetConstant(t4); 289 | SetConstant(t5); 290 | SetConstant(t6); 291 | SetConstant(t7); 292 | SetConstant(t8); 293 | SetConstant(t9); 294 | SetConstant(t10); 295 | SetConstant(t11); 296 | } 297 | } 298 | 299 | public class StrongFlow : StrongFlowBase, IStrongFlow 300 | { 301 | public StrongFlow(StrongFlowBase backing): base(backing) { 302 | } 303 | public StrongFlow(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11, T12 t12): base(new FlowBuilder()) { 304 | SetConstant(t1); 305 | SetConstant(t2); 306 | SetConstant(t3); 307 | SetConstant(t4); 308 | SetConstant(t5); 309 | SetConstant(t6); 310 | SetConstant(t7); 311 | SetConstant(t8); 312 | SetConstant(t9); 313 | SetConstant(t10); 314 | SetConstant(t11); 315 | SetConstant(t12); 316 | } 317 | } 318 | #endregion 319 | 320 | #region Interface 321 | 322 | public interface IStrongFlow : IHack, StrongFlow> 323 | { 324 | } 325 | 326 | public interface IStrongFlow : IHack, StrongFlow> 327 | { 328 | } 329 | 330 | public interface IStrongFlow : IHack, StrongFlow> 331 | { 332 | } 333 | 334 | public interface IStrongFlow : IHack, StrongFlow> 335 | { 336 | } 337 | 338 | public interface IStrongFlow : IHack, StrongFlow> 339 | { 340 | } 341 | 342 | public interface IStrongFlow : IHack, StrongFlow> 343 | { 344 | } 345 | 346 | public interface IStrongFlow : IHack, StrongFlow> 347 | { 348 | } 349 | 350 | public interface IStrongFlow : IHack, StrongFlow> 351 | { 352 | } 353 | 354 | public interface IStrongFlow : IHack, StrongFlow> 355 | { 356 | } 357 | 358 | public interface IStrongFlow : IHack, StrongFlow> 359 | { 360 | } 361 | 362 | public interface IStrongFlow : IHack, StrongFlow> 363 | { 364 | } 365 | 366 | public interface IStrongFlow : IHack, StrongFlow> 367 | { 368 | } 369 | #endregion 370 | 371 | } -------------------------------------------------------------------------------- /FunctionFlow.Strong/IHold.cs: -------------------------------------------------------------------------------- 1 | namespace Prototypist.FunctionFlow.Strong 2 | { 3 | 4 | public interface IHold : IHold 5 | { 6 | 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /FunctionFlow.Strong/StrongFlow.cs: -------------------------------------------------------------------------------- 1 |  2 | using System; 3 | using Prototypist.FunctionFlow; 4 | 5 | namespace Prototypist.FunctionFlow.Strong 6 | { 7 | 8 | public class StrongFlow : StrongFlowBase, IStrongFlow 9 | { 10 | public StrongFlow(StrongFlowBase backing):base(backing) 11 | { 12 | } 13 | public StrongFlow() : base(new FlowBuilder()) 14 | { 15 | } 16 | } 17 | 18 | // we need two conditions on the extension methods 19 | // we are limited to one condition per type 20 | // so we cature the type twice: 21 | // T1 == T2 22 | public interface IHack: IHold 23 | { 24 | } 25 | 26 | public interface IStrongFlow : IHack{} 27 | 28 | public abstract class StrongFlowBase : IHold 29 | { 30 | public StrongFlowBase(StrongFlowBase backing) 31 | { 32 | this.FlowBuilder = backing?.FlowBuilder ?? throw new ArgumentNullException(nameof(backing)); 33 | } 34 | 35 | public StrongFlowBase(FlowBuilder flowBuilder) 36 | { 37 | this.FlowBuilder = flowBuilder ?? throw new ArgumentNullException(nameof(flowBuilder)); 38 | } 39 | 40 | protected FlowBuilder FlowBuilder { get; } 41 | 42 | public T Run() { 43 | return FlowBuilder.Run(); 44 | } 45 | 46 | public (T1,T2) Run() 47 | { 48 | return FlowBuilder.Run(); 49 | } 50 | 51 | public (T1, T2, T3) Run() 52 | { 53 | return FlowBuilder.Run(); 54 | } 55 | public (T1, T2, T3, T4) Run() 56 | { 57 | return FlowBuilder.Run(); 58 | } 59 | public (T1, T2, T3, T4, T5) Run() 60 | { 61 | return FlowBuilder.Run(); 62 | } 63 | public (T1, T2, T3, T4, T5, T6) Run() 64 | { 65 | return FlowBuilder.Run(); 66 | } 67 | public (T1, T2, T3, T4, T5, T6, T7) Run() 68 | { 69 | return FlowBuilder.Run(); 70 | } 71 | 72 | public void AddStep(Delegate @delegate) { 73 | FlowBuilder.AddStep(@delegate); 74 | } 75 | 76 | public void AddStepPacked(Delegate @delegate) 77 | { 78 | FlowBuilder.AddStepPacked(@delegate); 79 | } 80 | public void AddStepPacked(Delegate @delegate) 81 | { 82 | FlowBuilder.AddStepPacked(@delegate); 83 | } 84 | public void AddStepPacked(Delegate @delegate) 85 | { 86 | FlowBuilder.AddStepPacked(@delegate); 87 | } 88 | public void AddStepPacked(Delegate @delegate) 89 | { 90 | FlowBuilder.AddStepPacked(@delegate); 91 | } 92 | public void AddStepPacked(Delegate @delegate) 93 | { 94 | FlowBuilder.AddStepPacked(@delegate); 95 | } 96 | public void AddStepPacked(Delegate @delegate) 97 | { 98 | FlowBuilder.AddStepPacked(@delegate); 99 | } 100 | public void SetConstant(T t) 101 | { 102 | FlowBuilder.SetConstant(t); 103 | } 104 | } 105 | 106 | public interface IHold { 107 | T Run(); 108 | (T1, T2) Run(); 109 | (T1, T2, T3) Run(); 110 | (T1, T2, T3, T4) Run(); 111 | (T1, T2, T3, T4, T5) Run(); 112 | (T1, T2, T3, T4, T5, T6) Run(); 113 | (T1, T2, T3, T4, T5, T6, T7) Run(); 114 | 115 | void AddStep(Delegate @delegate); 116 | 117 | void AddStepPacked(Delegate @delegate); 118 | void AddStepPacked(Delegate @delegate); 119 | void AddStepPacked(Delegate @delegate); 120 | void AddStepPacked(Delegate @delegate); 121 | void AddStepPacked(Delegate @delegate); 122 | void AddStepPacked(Delegate @delegate); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /FunctionFlow.Test/FlowBuilderTests.cs: -------------------------------------------------------------------------------- 1 |  2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Threading.Tasks; 7 | using Xunit; 8 | 9 | namespace Prototypist.FunctionFlow.Test 10 | { 11 | public class FlowBuilderTests 12 | { 13 | 14 | [Fact] 15 | public void ContainerTest() 16 | { 17 | var container = new TestConainer(); 18 | container.Dictionary[typeof(int)] = 5; 19 | 20 | var res = new FlowBuilder().Container(container).Run(); 21 | 22 | Assert.Equal(5, res); 23 | } 24 | 25 | [Fact] 26 | public void BlanketTest() 27 | { 28 | var finalInt = 0; 29 | var finalStr = ""; 30 | 31 | int inc(int x) => x + 1; 32 | string incStr(string x) => x + "1"; 33 | 34 | new FlowBuilder() 35 | .Then(() => 2) 36 | .Then((int i) => i + "") 37 | .Then(inc) 38 | .Then(inc) 39 | .Then(inc) 40 | .Then(inc) 41 | .Then(incStr) 42 | .Then(incStr) 43 | .Then(incStr) 44 | .Then(incStr) 45 | .Then((int x) => finalInt = x) 46 | .Then((string x) => finalStr = x) 47 | .Run(); 48 | 49 | Assert.Equal(6, finalInt); 50 | Assert.Equal("21111", finalStr); 51 | } 52 | 53 | [Fact] 54 | public void MultiReturn() 55 | { 56 | 57 | var (finalInt, finalStr) = new FlowBuilder() 58 | .Then(() => 2) 59 | .Then((int i) => i + "") 60 | .Then((int x) => x + 1) 61 | .Then((int x) => x + 1) 62 | .Then((int x) => x + 1) 63 | .Then((int x) => x + 1) 64 | .Then((string x) => x + "1") 65 | .Then((string x) => x + "1") 66 | .Then((string x) => x + "1") 67 | .Then((string x) => x + "1") 68 | .Run(); 69 | 70 | Assert.Equal(6, finalInt); 71 | Assert.Equal("21111", finalStr); 72 | } 73 | 74 | [Fact] 75 | public void ParameterTest() 76 | { 77 | var finalInt = 0; 78 | 79 | new FlowBuilder().Then(x => x + 1).Then(x => finalInt = x).Run(10); 80 | 81 | Assert.Equal(11, finalInt); 82 | } 83 | 84 | [Fact] 85 | public void PassThroughTest() 86 | { 87 | Assert.Equal(10, new FlowBuilder().Run(10)); 88 | } 89 | 90 | [Fact] 91 | public void SimpleTest() 92 | { 93 | Assert.Equal(1, new FlowBuilder().Then(() => 1).Run()); 94 | } 95 | 96 | [Fact] 97 | public void ReturnTest() 98 | { 99 | var res = new FlowBuilder().Then(x => 100 | x + 1 101 | ).Run(10); 102 | 103 | Assert.Equal(11, res); 104 | } 105 | 106 | [Fact] 107 | public void PackedThen() 108 | { 109 | var (name, value) = new FlowBuilder() 110 | .PackedThen(() => ("Time", 1282368345)) 111 | .Run(); 112 | 113 | Assert.Equal("Time", name); 114 | Assert.Equal(1282368345, value); 115 | } 116 | 117 | [Fact] 118 | public void SetTest() 119 | { 120 | var res = new FlowBuilder().Set(5).Run(); 121 | 122 | Assert.Equal(5, res); 123 | } 124 | 125 | [Fact] 126 | public void ReturnString() 127 | { 128 | 129 | var res = new FlowBuilder().Then(() => "test").Run(); 130 | 131 | Assert.Equal("test", res); 132 | } 133 | 134 | [Fact] 135 | public void SourceTest() 136 | { 137 | var res = new FlowBuilder().Source(() => 5 + "").Run(5); 138 | 139 | Assert.Equal("5", res); 140 | } 141 | 142 | [Fact] 143 | public void UnpackTest() 144 | { 145 | var (myInt, myStr) = new FlowBuilder().PackedThen(() => (2, "test")).Run(); 146 | 147 | Assert.Equal(2, myInt); 148 | Assert.Equal("test", myStr); 149 | 150 | } 151 | 152 | [Fact] 153 | public void CompileFuncTest() 154 | { 155 | var addOne = new FlowBuilder().Then(x => x + 1).Build>(); 156 | 157 | Assert.Equal(11, addOne(10)); 158 | Assert.Equal(0, addOne(-1)); 159 | } 160 | 161 | [Fact] 162 | public void CompileActionTest() 163 | { 164 | var finalInt = 0; 165 | 166 | var action = new FlowBuilder().Then(x => finalInt = x).Build>(); 167 | 168 | action(10); 169 | Assert.Equal(10, finalInt); 170 | action(0); 171 | Assert.Equal(0, finalInt); 172 | } 173 | 174 | [Fact] 175 | public void ParallelCompileActionTest() 176 | { 177 | var function = new FlowBuilder() 178 | .Then((int x) => x + 1) 179 | .Then((int x) => x + 1) 180 | .Then((int x) => x + 1) 181 | .Then((int x) => x + 1).Build>(); 182 | 183 | Parallel.For(0, 1000, (i) => 184 | { 185 | Assert.Equal(i + 4, function(i)); 186 | }); 187 | } 188 | 189 | [Fact] 190 | public void OutputOrdering() 191 | { 192 | var flow = new FlowBuilder().RunInParallel(); 193 | for (int j = 0; j < 100; j++) 194 | { 195 | var i = j; 196 | flow.Then(() => i); 197 | } 198 | Assert.Equal(99, flow.Run()); 199 | } 200 | 201 | [Fact] 202 | public void ParallelTest() 203 | { 204 | void wait() => Task.Delay(100).Wait(); 205 | 206 | var start = DateTime.Now.Ticks; 207 | 208 | new FlowBuilder() 209 | .RunInParallel() 210 | .Then(wait) 211 | .Then(wait) 212 | .Then(wait) 213 | .Then(wait) 214 | .Run(); 215 | 216 | var end = DateTime.Now.Ticks; 217 | 218 | Assert.InRange(TimeSpan.FromTicks(end - start), TimeSpan.FromSeconds(.1), TimeSpan.FromSeconds(.2)); 219 | 220 | } 221 | 222 | [Fact] 223 | public void ParallelPairs() { 224 | 225 | Func waitInt= i => { 226 | Task.Delay(100).Wait(); 227 | return i + 1; 228 | }; 229 | 230 | Func waitString = s => { 231 | Task.Delay(100).Wait(); 232 | return s + "1"; 233 | }; 234 | 235 | var start = DateTime.Now.Ticks; 236 | 237 | var (resInt,resStr)= new FlowBuilder() 238 | .RunInParallel() 239 | .Then(waitInt) 240 | .Then(waitInt) 241 | .Then(waitInt) 242 | .Then(waitString) 243 | .Then(waitString) 244 | .Then(waitString) 245 | .Run(0,""); 246 | 247 | var end = DateTime.Now.Ticks; 248 | 249 | Assert.Equal(3, resInt); 250 | Assert.Equal("111", resStr); 251 | Assert.InRange(TimeSpan.FromTicks(end - start), TimeSpan.FromSeconds(.3), TimeSpan.FromSeconds(.4)); 252 | } 253 | 254 | [Fact] 255 | public void ParallelPairsLongShort() 256 | { 257 | 258 | Func waitIntLong = i => { 259 | Task.Delay(100).Wait(); 260 | return i + 1; 261 | }; 262 | 263 | Func waitIntShort = i => { 264 | return i + 1; 265 | }; 266 | 267 | Func waitStringLong = s => { 268 | Task.Delay(100).Wait(); 269 | return s + "1"; 270 | }; 271 | 272 | Func waitStringShort = i => { 273 | return i + "1"; 274 | }; 275 | 276 | var start = DateTime.Now.Ticks; 277 | 278 | var (resInt, resStr) = new FlowBuilder() 279 | .RunInParallel() 280 | .Then(waitIntLong) 281 | .Then(waitIntShort) 282 | .Then(waitIntLong) 283 | .Then(waitIntShort) 284 | .Then(waitStringShort) 285 | .Then(waitStringLong) 286 | .Then(waitStringShort) 287 | .Then(waitStringLong) 288 | .Run(0, ""); 289 | 290 | var end = DateTime.Now.Ticks; 291 | 292 | Assert.Equal(4, resInt); 293 | Assert.Equal("1111", resStr); 294 | Assert.InRange(TimeSpan.FromTicks(end - start), TimeSpan.FromSeconds(.2), TimeSpan.FromSeconds(.3)); 295 | } 296 | 297 | [Fact] 298 | public void NoParallelTest() 299 | { 300 | void wait() => Task.Delay(100).Wait(); 301 | 302 | var start = DateTime.Now.Ticks; 303 | 304 | new FlowBuilder() 305 | .Then(wait) 306 | .Then(wait) 307 | .Then(wait) 308 | .Run(); 309 | 310 | var end = DateTime.Now.Ticks; 311 | 312 | Assert.InRange(TimeSpan.FromTicks(end - start), TimeSpan.FromSeconds(.2), TimeSpan.FromSeconds(.4)); 313 | 314 | } 315 | 316 | 317 | 318 | } 319 | 320 | 321 | #region Help 322 | 323 | internal class TestConainer : IContainer 324 | { 325 | public Dictionary Dictionary = new Dictionary(); 326 | public T Get() 327 | { 328 | return (T)Dictionary[typeof(T)]; 329 | } 330 | } 331 | 332 | #endregion 333 | 334 | } 335 | -------------------------------------------------------------------------------- /FunctionFlow.Test/FunctionFlow.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | 6 | false 7 | 8 | Prototypist.FunctionFlow.Test 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | all 18 | runtime; build; native; contentfiles; analyzers; buildtransitive 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /FunctionFlow.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27004.2005 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionFlow", "FunctionFlow\FunctionFlow.csproj", "{A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionFlow.Strong", "FunctionFlow.Strong\FunctionFlow.Strong.csproj", "{C66A1319-5451-4C86-BB8D-75E71D6315C1}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionFlow.Strong.CodeGen", "FunctionFlow.Strong.CodeGen\FunctionFlow.Strong.CodeGen.csproj", "{EEE2D861-3015-4143-83E8-73D4EC1F7B51}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionFlow.Test", "FunctionFlow.Test\FunctionFlow.Test.csproj", "{8836C40A-27C9-40A2-842D-8945366E86AB}" 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5778BB29-8A72-482C-A84E-13258FC7E99F}" 15 | ProjectSection(SolutionItems) = preProject 16 | icon.png = icon.png 17 | icon.svg = icon.svg 18 | ReadMe.md = ReadMe.md 19 | EndProjectSection 20 | EndProject 21 | Global 22 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 23 | Debug|Any CPU = Debug|Any CPU 24 | Debug|ARM = Debug|ARM 25 | Debug|x64 = Debug|x64 26 | Debug|x86 = Debug|x86 27 | Release|Any CPU = Release|Any CPU 28 | Release|ARM = Release|ARM 29 | Release|x64 = Release|x64 30 | Release|x86 = Release|x86 31 | EndGlobalSection 32 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 33 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Debug|ARM.ActiveCfg = Debug|Any CPU 36 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Debug|ARM.Build.0 = Debug|Any CPU 37 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Debug|x64.ActiveCfg = Debug|Any CPU 38 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Debug|x64.Build.0 = Debug|Any CPU 39 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Debug|x86.ActiveCfg = Debug|Any CPU 40 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Debug|x86.Build.0 = Debug|Any CPU 41 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Release|Any CPU.Build.0 = Release|Any CPU 43 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Release|ARM.ActiveCfg = Release|Any CPU 44 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Release|ARM.Build.0 = Release|Any CPU 45 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Release|x64.ActiveCfg = Release|Any CPU 46 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Release|x64.Build.0 = Release|Any CPU 47 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Release|x86.ActiveCfg = Release|Any CPU 48 | {A6283C7F-3DC2-403C-AE47-E5AD924C6A5F}.Release|x86.Build.0 = Release|Any CPU 49 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 50 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Debug|Any CPU.Build.0 = Debug|Any CPU 51 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Debug|ARM.ActiveCfg = Debug|Any CPU 52 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Debug|ARM.Build.0 = Debug|Any CPU 53 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Debug|x64.ActiveCfg = Debug|Any CPU 54 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Debug|x64.Build.0 = Debug|Any CPU 55 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Debug|x86.ActiveCfg = Debug|Any CPU 56 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Debug|x86.Build.0 = Debug|Any CPU 57 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Release|Any CPU.ActiveCfg = Release|Any CPU 58 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Release|Any CPU.Build.0 = Release|Any CPU 59 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Release|ARM.ActiveCfg = Release|Any CPU 60 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Release|ARM.Build.0 = Release|Any CPU 61 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Release|x64.ActiveCfg = Release|Any CPU 62 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Release|x64.Build.0 = Release|Any CPU 63 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Release|x86.ActiveCfg = Release|Any CPU 64 | {C66A1319-5451-4C86-BB8D-75E71D6315C1}.Release|x86.Build.0 = Release|Any CPU 65 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 66 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Debug|Any CPU.Build.0 = Debug|Any CPU 67 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Debug|ARM.ActiveCfg = Debug|Any CPU 68 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Debug|ARM.Build.0 = Debug|Any CPU 69 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Debug|x64.ActiveCfg = Debug|Any CPU 70 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Debug|x64.Build.0 = Debug|Any CPU 71 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Debug|x86.ActiveCfg = Debug|Any CPU 72 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Debug|x86.Build.0 = Debug|Any CPU 73 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Release|Any CPU.ActiveCfg = Release|Any CPU 74 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Release|Any CPU.Build.0 = Release|Any CPU 75 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Release|ARM.ActiveCfg = Release|Any CPU 76 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Release|ARM.Build.0 = Release|Any CPU 77 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Release|x64.ActiveCfg = Release|Any CPU 78 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Release|x64.Build.0 = Release|Any CPU 79 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Release|x86.ActiveCfg = Release|Any CPU 80 | {EEE2D861-3015-4143-83E8-73D4EC1F7B51}.Release|x86.Build.0 = Release|Any CPU 81 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 82 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Debug|Any CPU.Build.0 = Debug|Any CPU 83 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Debug|ARM.ActiveCfg = Debug|Any CPU 84 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Debug|ARM.Build.0 = Debug|Any CPU 85 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Debug|x64.ActiveCfg = Debug|Any CPU 86 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Debug|x64.Build.0 = Debug|Any CPU 87 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Debug|x86.ActiveCfg = Debug|Any CPU 88 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Debug|x86.Build.0 = Debug|Any CPU 89 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Release|Any CPU.ActiveCfg = Release|Any CPU 90 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Release|ARM.ActiveCfg = Release|Any CPU 91 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Release|ARM.Build.0 = Release|Any CPU 92 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Release|x64.ActiveCfg = Release|Any CPU 93 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Release|x64.Build.0 = Release|Any CPU 94 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Release|x86.ActiveCfg = Release|Any CPU 95 | {8836C40A-27C9-40A2-842D-8945366E86AB}.Release|x86.Build.0 = Release|Any CPU 96 | EndGlobalSection 97 | GlobalSection(SolutionProperties) = preSolution 98 | HideSolutionNode = FALSE 99 | EndGlobalSection 100 | GlobalSection(ExtensibilityGlobals) = postSolution 101 | SolutionGuid = {F2A2ABE7-D16A-4A35-ABFD-EBE79D6E35C3} 102 | EndGlobalSection 103 | EndGlobal 104 | -------------------------------------------------------------------------------- /FunctionFlow/ExpressionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Linq.Expressions; 2 | using System.Reflection; 3 | 4 | namespace Prototypist.FunctionFlow 5 | { 6 | internal static class ExpressionExtensions{ 7 | public static string GetDebugView(this Expression exp) 8 | { 9 | if (exp == null) 10 | return null; 11 | 12 | var propertyInfo = typeof(Expression).GetProperty("DebugView", BindingFlags.Instance | BindingFlags.NonPublic); 13 | return propertyInfo.GetValue(exp) as string; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /FunctionFlow/ExpressionInfo.cs: -------------------------------------------------------------------------------- 1 |  2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | 7 | namespace Prototypist.FunctionFlow 8 | { 9 | public partial class FlowBuilder 10 | { 11 | private class ExpressionInfo 12 | { 13 | public readonly Expression backing; 14 | public readonly Expression[] Inputs; 15 | public Expression Result = null; 16 | public ParameterExpression task = null; 17 | public readonly List waitsOn = new List(); 18 | public readonly Type[] returnTypes; 19 | public readonly Type[] ParameterTypes; 20 | 21 | public ExpressionInfo(WorkItem workItem) 22 | { 23 | this.backing = Expression.Constant(workItem.todo) ?? throw new ArgumentNullException(nameof(workItem)); 24 | this.returnTypes = workItem.returnTypes; 25 | this.ParameterTypes = workItem.todo.GetType().Parameters(); 26 | this.Inputs = new Expression[ParameterTypes.Count()]; 27 | } 28 | 29 | 30 | /// returnTypes must not be null 31 | internal bool SameOutput(ExpressionInfo expressionInfo) => 32 | returnTypes?.SequenceEqual(expressionInfo.returnTypes) ?? false; 33 | internal bool WillWaitFor(ExpressionInfo sourceExpressionInfo) { 34 | if (sourceExpressionInfo == this) { 35 | return true; 36 | } 37 | return waitsOn?.Any(x => x.WillWaitFor(sourceExpressionInfo)) ?? false; 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /FunctionFlow/FlowBuilder.cs: -------------------------------------------------------------------------------- 1 |  2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Prototypist.FunctionFlow 11 | { 12 | public interface IContainer 13 | { 14 | T Get(); 15 | } 16 | 17 | public partial class FlowBuilder : IFlowBuilder 18 | { 19 | public bool Parallel { get; set; } = false; 20 | 21 | private readonly Dictionary sources = new Dictionary(); 22 | private readonly Dictionary constants = new Dictionary(); 23 | private Func fallbackSource; 24 | private readonly List todo = new List(); 25 | 26 | public FlowBuilder() 27 | { 28 | } 29 | 30 | #region Configure 31 | 32 | public void SetContainer(IContainer container) 33 | { 34 | var containerParameter = Expression.Constant(container); 35 | var get = typeof(IContainer).GetMethods().Where(x => x.Name == "Get" && x.IsGenericMethod).Single(); 36 | fallbackSource = (type) => Expression.Call(containerParameter, get.MakeGenericMethod(type)); 37 | } 38 | 39 | public void SetConstant(T t) 40 | { 41 | var key = typeof(T); 42 | if (sources.ContainsKey(key)) { 43 | throw new Exception($"Source already set for {key}"); 44 | } 45 | constants.Add(key, Expression.Constant(t)); 46 | } 47 | 48 | // you can make sources loop pretty easy 49 | // A -> B 50 | // B -> A 51 | // or just 52 | // A -> A 53 | // is it worth reflecting around and blocking loops? 54 | // probably atleast worth throwing in BuildExpression 55 | public void SetSource(T t) 56 | where T : Delegate 57 | { 58 | var key = typeof(T).FuncReturnOrNull(); 59 | if (constants.ContainsKey(key)) 60 | { 61 | throw new Exception($"Constant already set for {key}"); 62 | } 63 | sources.Add(key, t); 64 | } 65 | 66 | public void AddStep(Delegate expression) 67 | { 68 | var returnType = expression.GetType().FuncReturnOrNull(); 69 | if (returnType == null) 70 | { 71 | todo.Add(new WorkItem(expression, new Type[0])); 72 | } 73 | else { 74 | todo.Add(new WorkItem(expression, new[] { returnType })); 75 | } 76 | } 77 | 78 | public void AddStepPacked(Delegate expression) 79 | { 80 | todo.Add(new WorkItem(expression, new[] { typeof(T1), typeof(T2) })); 81 | } 82 | 83 | public void AddStepPacked(Delegate expression) 84 | { 85 | todo.Add(new WorkItem(expression, new[] { typeof(T1), typeof(T2), typeof(T3) })); 86 | } 87 | 88 | public void AddStepPacked(Delegate expression) 89 | { 90 | todo.Add(new WorkItem(expression, new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) })); 91 | } 92 | 93 | public void AddStepPacked(Delegate expression) 94 | { 95 | todo.Add(new WorkItem(expression, new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5) })); 96 | } 97 | 98 | public void AddStepPacked(Delegate expression) 99 | { 100 | todo.Add(new WorkItem(expression, new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6) })); 101 | } 102 | 103 | public void AddStepPacked(Delegate expression) 104 | { 105 | todo.Add(new WorkItem(expression, new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7) })); 106 | } 107 | 108 | #endregion 109 | 110 | #region Build/Run 111 | 112 | public void Run(params object[] inputs) 113 | { 114 | var parameters = inputs.Select(x => Expression.Parameter(x.GetType())).ToArray(); 115 | 116 | BuildExpression( 117 | FromParams(parameters, FromConstants(FromSources(FromContainer()))), 118 | Parallel, 119 | new Type[0], 120 | NoReturn(), 121 | parameters, 122 | todo).Compile().DynamicInvoke(inputs); 123 | } 124 | 125 | public T Run(params object[] inputs) 126 | { 127 | var parameters = inputs.Select(x => Expression.Parameter(x.GetType())).ToArray(); 128 | 129 | return (T)(BuildExpression( 130 | FromParams(parameters, FromConstants(FromSources(FromContainer()))), 131 | Parallel, 132 | new[] { typeof(T) }, 133 | SingleReturn(typeof(T)), 134 | parameters, 135 | todo).Compile().DynamicInvoke(inputs)); 136 | } 137 | 138 | public (T1, T2) Run(params object[] inputs) 139 | { 140 | var parameters = inputs.Select(x => Expression.Parameter(x.GetType())).ToArray(); 141 | 142 | return (ValueTuple)(BuildExpression( 143 | FromParams(parameters, FromConstants(FromSources(FromContainer()))), 144 | Parallel, 145 | new[] { typeof(T1), typeof(T2) }, 146 | MultiReturn(typeof(ValueTuple)), 147 | parameters, 148 | todo).Compile().DynamicInvoke(inputs)); 149 | } 150 | 151 | public (T1, T2, T3) Run(params object[] inputs) 152 | { 153 | var parameters = inputs.Select(x => Expression.Parameter(x.GetType())).ToArray(); 154 | 155 | return (ValueTuple)(BuildExpression( 156 | FromParams(parameters, FromConstants(FromSources(FromContainer()))), 157 | Parallel, 158 | new[] { typeof(T1), typeof(T2), typeof(T3) }, 159 | MultiReturn(typeof(ValueTuple)), 160 | parameters, 161 | todo).Compile().DynamicInvoke(inputs)); 162 | } 163 | 164 | public (T1, T2, T3, T4) Run(params object[] inputs) 165 | { 166 | var parameters = inputs.Select(x => Expression.Parameter(x.GetType())).ToArray(); 167 | 168 | return (ValueTuple)(BuildExpression( 169 | FromParams(parameters, FromConstants(FromSources(FromContainer()))), 170 | Parallel, 171 | new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, 172 | MultiReturn(typeof(ValueTuple)), 173 | parameters, 174 | todo).Compile().DynamicInvoke(inputs)); 175 | } 176 | 177 | public (T1, T2, T3, T4, T5) Run(params object[] inputs) 178 | { 179 | var parameters = inputs.Select(x => Expression.Parameter(x.GetType())).ToArray(); 180 | 181 | return (ValueTuple)(BuildExpression( 182 | FromParams(parameters, FromConstants(FromSources(FromContainer()))), 183 | Parallel, 184 | new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5) }, 185 | MultiReturn(typeof(ValueTuple)), 186 | parameters, 187 | todo).Compile().DynamicInvoke(inputs)); 188 | } 189 | 190 | public (T1, T2, T3, T4, T5, T6) Run(params object[] inputs) 191 | { 192 | var parameters = inputs.Select(x => Expression.Parameter(x.GetType())).ToArray(); 193 | 194 | return (ValueTuple)(BuildExpression( 195 | FromParams(parameters, FromConstants(FromSources(FromContainer()))), 196 | Parallel, 197 | new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6) }, 198 | MultiReturn(typeof(ValueTuple)), 199 | parameters, 200 | todo).Compile().DynamicInvoke(inputs)); 201 | } 202 | 203 | public (T1, T2, T3, T4, T5, T6, T7) Run(params object[] inputs) 204 | { 205 | var parameters = inputs.Select(x => Expression.Parameter(x.GetType())).ToArray(); 206 | 207 | return (ValueTuple)(BuildExpression( 208 | FromParams(parameters, FromConstants(FromSources(FromContainer()))), 209 | Parallel, 210 | new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7) }, 211 | MultiReturn(typeof(ValueTuple)), 212 | parameters, 213 | todo).Compile().DynamicInvoke(inputs)); 214 | } 215 | 216 | public T Build() where T : Delegate 217 | { 218 | var parameters = typeof(T).Parameters().Select(x => Expression.Parameter(x)).ToArray(); 219 | 220 | return (T)BuildExpression( 221 | FromParams(parameters, FromConstants(FromSources(FromContainer()))), 222 | Parallel, 223 | GetReturns(typeof(T)), 224 | typeof(T).FuncReturnOrNull() != null ? SingleReturn(typeof(T).FuncReturnOrNull()) : NoReturn(), 225 | parameters, 226 | todo).Compile(); 227 | } 228 | 229 | #endregion 230 | 231 | #region Build/Run Help 232 | 233 | private Func, Expression> FromParams(ParameterExpression[] parameters, Func, Expression> fallback) 234 | { 235 | return (type, searchCurrentContext) => 236 | { 237 | var res = parameters.SingleOrDefault(x => x.Type == type); 238 | if (res == null) 239 | { 240 | return fallback(type, searchCurrentContext); 241 | } 242 | return res; 243 | }; 244 | } 245 | 246 | private Func, Expression> FromConstants(Func, Expression> fallback) 247 | { 248 | return (type, searchCurrent) => 249 | { 250 | if (constants.TryGetValue(type, out var cons)) 251 | { 252 | return cons; 253 | } 254 | return fallback(type, searchCurrent); 255 | }; 256 | } 257 | 258 | private Func, Expression> FromSources(Func, Expression> fallback) 259 | { 260 | return (type, searchCurrent) => 261 | { 262 | if (sources.TryGetValue(type, out var source)) 263 | { 264 | var parms = source.GetType().Parameters().Select(x => searchCurrent(x)).ToArray(); 265 | var toCall = Expression.Constant(source); 266 | return Expression.Invoke(toCall, parms); 267 | } 268 | return fallback(type, searchCurrent); 269 | }; 270 | } 271 | 272 | private Func, Expression> FromContainer() 273 | { 274 | return (type, searchCurrent) => 275 | { 276 | if (fallbackSource == null) 277 | { 278 | throw new Exception("Parameter not found"); 279 | } 280 | 281 | return fallbackSource(type); 282 | }; 283 | } 284 | 285 | private static Type[] GetReturns(Type type) 286 | { 287 | 288 | var returnType = type.FuncReturnOrNull(); 289 | 290 | if (returnType == null) 291 | { 292 | return new Type[0]; 293 | } 294 | return new Type[] { returnType }; 295 | } 296 | 297 | private static Func NoReturn() 298 | { 299 | return x => new Expression[0]; 300 | } 301 | 302 | private static Func SingleReturn(Type type) 303 | { 304 | return x => 305 | { 306 | return new Expression[]{ 307 | x.Single() 308 | }; 309 | }; 310 | } 311 | 312 | private Func MultiReturn(Type type) 313 | { 314 | return x => 315 | { 316 | var constructor = type.GetConstructor(type.GetGenericArguments()); 317 | return new Expression[] { 318 | Expression.New(constructor, x) 319 | }; 320 | }; 321 | } 322 | 323 | private static Func FromInputs(object[] inputs) 324 | { 325 | 326 | var parameters = inputs.Select(x => Expression.Constant(x, x.GetType())).ToArray(); 327 | 328 | return type => parameters.Single(x => x.Type == type); 329 | } 330 | 331 | 332 | #endregion 333 | 334 | private static LambdaExpression BuildExpression( 335 | Func, Expression> getParameter, 336 | bool parallel, 337 | Type[] returns, 338 | Func addReturn, 339 | ParameterExpression[] parameters, 340 | List todo) 341 | { 342 | var resultsDict = new Dictionary(); 343 | 344 | var expressionInfos = todo.Select(x => new ExpressionInfo(x)).ToList(); 345 | 346 | // discover dependency chain 347 | for (int i = 0; i < expressionInfos.Count; i++) 348 | { 349 | var expressionInfo = expressionInfos[i]; 350 | for (var parmIndex = 0; parmIndex < expressionInfo.ParameterTypes.Length; parmIndex++) 351 | { 352 | var parmType = expressionInfo.ParameterTypes[parmIndex]; 353 | expressionInfo.Inputs[parmIndex] = FindInput(expressionInfo, parmType, i); 354 | } 355 | if (expressionInfo.returnTypes != null && expressionInfo.returnTypes.Count() != 0) { 356 | for (int j = i - 1; j >= 0; j--) 357 | { 358 | var otherExpressionInfo = expressionInfos[j]; 359 | if (otherExpressionInfo.SameOutput(expressionInfo)) { 360 | if (!expressionInfo.WillWaitFor(otherExpressionInfo)) 361 | { 362 | expressionInfo.waitsOn.Add(otherExpressionInfo); 363 | } 364 | } 365 | } 366 | } 367 | } 368 | 369 | var results = new Expression[returns.Length]; 370 | 371 | for (int i = 0; i < returns.Length; i++) 372 | { 373 | var type = returns[i]; 374 | results[i] = FindInput(null, type, expressionInfos.Count); 375 | } 376 | 377 | var body = expressionInfos.Select(expressionInfo => { 378 | if (parallel) 379 | { 380 | 381 | expressionInfo.task = Expression.Parameter(typeof(Task)); 382 | var tasks = expressionInfo.waitsOn.Select(x => x.task).Cast().ToArray(); 383 | if (tasks.Any()) 384 | { 385 | var whenAll = taskWhenAll.Value; 386 | var wait = Expression.Call(whenAll, Expression.NewArrayInit(typeof(Task), tasks)); 387 | 388 | var lambda = MakeTaskLambda(expressionInfo); 389 | var run = Expression.Call(wait, taskContinueWith.Value, lambda); 390 | return Expression.Assign(expressionInfo.task, run); 391 | } 392 | else { 393 | var lambda = MakeLambda(expressionInfo); 394 | return Expression.Assign(expressionInfo.task, Expression.Call(taskRun.Value, lambda)); 395 | } 396 | } 397 | return MakeSingle(expressionInfo); 398 | }).ToList(); 399 | 400 | if (parallel) { 401 | var tasks = expressionInfos.Select(x => x.task).ToArray(); 402 | var whenAll = Expression.Call(taskWhenAll.Value, Expression.NewArrayInit(typeof(Task), tasks)); 403 | var wait = Expression.Call(whenAll, taskWait.Value); 404 | body.Add(wait); 405 | } 406 | 407 | body.AddRange(addReturn(results)); 408 | 409 | var extraParametersBuilder = resultsDict.Select(x => x.Value); 410 | if (parallel) 411 | { 412 | extraParametersBuilder = extraParametersBuilder.Concat(expressionInfos.Select(x => x.task)); 413 | } 414 | var extraParameters = extraParametersBuilder.ToArray(); 415 | 416 | var allParameters = extraParameters.Concat(parameters); 417 | 418 | var inner = Expression.Lambda(Expression.Block(body), allParameters); 419 | 420 | if (!extraParameters.Any()) { 421 | return inner; 422 | } 423 | 424 | var agrs = extraParameters.Select(x => Expression.Constant(GetDefault(x.Type),x.Type)).Concat(parameters.Cast()).ToArray(); 425 | var final = Expression.Lambda(Expression.Invoke(inner, agrs), parameters); 426 | 427 | return final; 428 | 429 | #region Help 430 | 431 | Expression MakeTaskLambda(ExpressionInfo target) { 432 | return Expression.Lambda>(MakeSingle(target), new ParameterExpression[] { Expression.Parameter(typeof(Task)) }); 433 | } 434 | 435 | 436 | Expression MakeLambda(ExpressionInfo target) 437 | { 438 | var expression = MakeSingle(target); 439 | 440 | if (expression is Expression) { 441 | return expression; 442 | } 443 | 444 | return Expression.Lambda(expression); 445 | } 446 | 447 | Expression MakeSingle(ExpressionInfo target) 448 | { 449 | var innerParameters = new List(); 450 | for (var i = 0; i < target.ParameterTypes.Length; i++) 451 | { 452 | var input = target.Inputs[i]; 453 | innerParameters.Add(input); 454 | } 455 | if (target.Result != null) 456 | { 457 | return Expression.Assign(target.Result, Expression.Invoke(target.backing, innerParameters)); 458 | } 459 | else 460 | { 461 | return Expression.Invoke(target.backing, innerParameters); 462 | } 463 | } 464 | 465 | Expression GetVar(Type t) 466 | { 467 | if (resultsDict.TryGetValue(t, out var res)) 468 | { 469 | return res; 470 | } 471 | else { 472 | var newVal = Expression.Parameter(t,t.Name.ToLower()); 473 | resultsDict[t] = newVal; 474 | return newVal; 475 | } 476 | } 477 | 478 | Expression FindInput(ExpressionInfo expressionOrNull, Type parmType, int startAt) 479 | { 480 | Expression input = null; 481 | for (int j = startAt - 1; j >= 0; j--) 482 | { 483 | var sourceExpressionInfo = expressionInfos[j]; 484 | if (sourceExpressionInfo.returnTypes.Length > 1) 485 | { 486 | // sourceExpressionInfo.ReturnType is valueTuple 487 | // we need to walk over it's members 488 | for (int k = 0; k < sourceExpressionInfo.returnTypes.Length; k++) 489 | { 490 | if (sourceExpressionInfo.returnTypes[k] == parmType) 491 | { 492 | if (!(expressionOrNull?.WillWaitFor(sourceExpressionInfo) ?? true)) 493 | { 494 | expressionOrNull?.waitsOn?.Add(sourceExpressionInfo); 495 | } 496 | var tupleType = valueTypeArray.Value[sourceExpressionInfo.returnTypes.Length - 1].MakeGenericType(sourceExpressionInfo.returnTypes); 497 | if (sourceExpressionInfo.Result == null) 498 | { 499 | sourceExpressionInfo.Result = GetVar(tupleType); 500 | } 501 | if (input == null) 502 | { 503 | input = Expression.Field(sourceExpressionInfo.Result, tupleType.GetField("Item" + (k + 1))); 504 | } 505 | } 506 | } 507 | } 508 | else 509 | { 510 | if (sourceExpressionInfo.returnTypes[0] == parmType) 511 | { 512 | if (!(expressionOrNull?.WillWaitFor(sourceExpressionInfo)?? true)) 513 | { 514 | expressionOrNull?.waitsOn?.Add(sourceExpressionInfo); 515 | } 516 | if (sourceExpressionInfo.Result == null) 517 | { 518 | sourceExpressionInfo.Result = GetVar(parmType); 519 | } 520 | if (input == null) 521 | { 522 | input = sourceExpressionInfo.Result; 523 | } 524 | } 525 | } 526 | } 527 | 528 | if (input == null) 529 | { 530 | input = getParameter(parmType, t => FindInput(null, t, startAt)); 531 | } 532 | 533 | return input; 534 | 535 | } 536 | 537 | object GetDefault(Type type) 538 | { 539 | if (type.IsValueType) 540 | { 541 | return Activator.CreateInstance(type); 542 | } 543 | return null; 544 | } 545 | 546 | #endregion 547 | } 548 | 549 | #region Static 550 | 551 | private static Lazy taskWait = new Lazy(() => typeof(Task) 552 | .GetMethods() 553 | .Where(x => 554 | x.Name == "Wait" && 555 | !x.IsGenericMethod && 556 | x.GetParameters().Count() == 0) 557 | .Single()); 558 | 559 | private static Lazy taskWhenAll = new Lazy(() => typeof(Task) 560 | .GetMethods() 561 | .Where(x => 562 | x.Name == "WhenAll" && 563 | !x.IsGenericMethod && 564 | x.GetParameters().Count() == 1 565 | && x.GetParameters().Single().ParameterType == typeof(Task[])) 566 | .Single()); 567 | 568 | private static Lazy taskContinueWith = new Lazy(() => typeof(Task) 569 | .GetMethods() 570 | .Where(x => 571 | x.Name == "ContinueWith" && 572 | !x.IsGenericMethod && 573 | x.GetParameters().Count() == 1) 574 | .Single()); 575 | 576 | private static Lazy taskRun = new Lazy(() => typeof(Task) 577 | .GetMethods() 578 | .Where(x => 579 | x.Name == "Run" && 580 | !x.IsGenericMethod && 581 | x.GetParameters().Count() == 1 && 582 | x.GetParameters().Single().ParameterType == typeof(Action)) 583 | .Single()); 584 | 585 | private static readonly Lazy valueTypeArray = new Lazy(() => { 586 | return new Type[]{ 587 | typeof(ValueTuple<>), 588 | typeof(ValueTuple<,>), 589 | typeof(ValueTuple<,,>), 590 | typeof(ValueTuple<,,,>), 591 | typeof(ValueTuple<,,,,>), 592 | typeof(ValueTuple<,,,,,>), 593 | }; 594 | }); 595 | 596 | private static readonly Lazy actionArray = new Lazy(() => { 597 | return new Type[]{ 598 | typeof(Action), 599 | typeof(Action<>), 600 | typeof(Action<,>), 601 | typeof(Action<,,>), 602 | typeof(Action<,,,>), 603 | typeof(Action<,,,,>), 604 | typeof(Action<,,,,,>), 605 | typeof(Action<,,,,,,>), 606 | typeof(Action<,,,,,,,>), 607 | typeof(Action<,,,,,,,,>), 608 | typeof(Action<,,,,,,,,,>), 609 | typeof(Action<,,,,,,,,,,>), 610 | typeof(Action<,,,,,,,,,,,>), 611 | typeof(Action<,,,,,,,,,,,,>), 612 | typeof(Action<,,,,,,,,,,,,,>), 613 | typeof(Action<,,,,,,,,,,,,,,>), 614 | typeof(Action<,,,,,,,,,,,,,,,>), 615 | }; 616 | }); 617 | 618 | #endregion 619 | } 620 | } 621 | -------------------------------------------------------------------------------- /FunctionFlow/FlowBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using System.Threading.Tasks; 5 | 6 | namespace Prototypist.FunctionFlow 7 | { 8 | public static partial class FlowBuilderExtensions { 9 | 10 | 11 | public static IFlowBuilder Set(this IFlowBuilder self, T t) { 12 | self.SetConstant(t); 13 | return self; 14 | } 15 | 16 | public static IFlowBuilder Container(this IFlowBuilder self, IContainer container) 17 | { 18 | self.SetContainer(container); 19 | return self; 20 | } 21 | 22 | public static IFlowBuilder RunInParallel(this IFlowBuilder self) 23 | { 24 | self.Parallel = true; 25 | return self; 26 | } 27 | 28 | #region Source 29 | 30 | public static IFlowBuilder Source(this IFlowBuilder self, Func func) 31 | { 32 | self.SetSource(func); 33 | return self; 34 | } 35 | 36 | public static IFlowBuilder Source(this IFlowBuilder self, Func func) 37 | { 38 | self.SetSource(func); 39 | return self; 40 | } 41 | 42 | public static IFlowBuilder Source(this IFlowBuilder self, Func func) 43 | { 44 | self.SetSource(func); 45 | return self; 46 | } 47 | 48 | public static IFlowBuilder Source(this IFlowBuilder self, Func func) 49 | { 50 | self.SetSource(func); 51 | return self; 52 | } 53 | 54 | public static IFlowBuilder Source(this IFlowBuilder self, Func func) 55 | { 56 | self.SetSource(func); 57 | return self; 58 | } 59 | 60 | public static IFlowBuilder Source(this IFlowBuilder self, Func func) 61 | { 62 | self.SetSource(func); 63 | return self; 64 | } 65 | 66 | public static IFlowBuilder Source(this IFlowBuilder self, Func func) 67 | { 68 | self.SetSource(func); 69 | return self; 70 | } 71 | 72 | public static IFlowBuilder Source(this IFlowBuilder self, Func func) 73 | { 74 | self.SetSource(func); 75 | return self; 76 | } 77 | 78 | public static IFlowBuilder Source(this IFlowBuilder self, Func func) 79 | { 80 | self.SetSource(func); 81 | return self; 82 | } 83 | 84 | public static IFlowBuilder Source(this IFlowBuilder self, Func func) 85 | { 86 | self.SetSource(func); 87 | return self; 88 | } 89 | 90 | public static IFlowBuilder Source(this IFlowBuilder self, Func func) 91 | { 92 | self.SetSource(func); 93 | return self; 94 | } 95 | 96 | public static IFlowBuilder Source(this IFlowBuilder self, Func func) 97 | { 98 | self.SetSource(func); 99 | return self; 100 | } 101 | 102 | public static IFlowBuilder Source(this IFlowBuilder self, Func func) 103 | { 104 | self.SetSource(func); 105 | return self; 106 | } 107 | 108 | public static IFlowBuilder Source(this IFlowBuilder self, Func func) 109 | { 110 | self.SetSource(func); 111 | return self; 112 | } 113 | 114 | public static IFlowBuilder Source(this IFlowBuilder self, Func func) 115 | { 116 | self.SetSource(func); 117 | return self; 118 | } 119 | 120 | public static IFlowBuilder Source(this IFlowBuilder self, Func func) 121 | { 122 | self.SetSource(func); 123 | return self; 124 | } 125 | 126 | public static IFlowBuilder Source(this IFlowBuilder self, Func func) 127 | { 128 | self.SetSource(func); 129 | return self; 130 | } 131 | 132 | #endregion 133 | 134 | #region Then Action 135 | 136 | public static IFlowBuilder Then(this IFlowBuilder self, Action todo) 137 | { 138 | self.AddStep(todo); 139 | return self; 140 | } 141 | 142 | public static IFlowBuilder Then(this IFlowBuilder self, Action todo) 143 | { 144 | self.AddStep(todo); 145 | return self; 146 | } 147 | 148 | public static IFlowBuilder Then(this IFlowBuilder self, Action todo) 149 | { 150 | self.AddStep(todo); 151 | return self; 152 | } 153 | 154 | public static IFlowBuilder Then(this IFlowBuilder self, Action todo) 155 | { 156 | self.AddStep(todo); 157 | return self; 158 | } 159 | 160 | public static IFlowBuilder Then(this IFlowBuilder self, Action todo) 161 | { 162 | self.AddStep(todo); 163 | return self; 164 | } 165 | 166 | public static IFlowBuilder Then(this IFlowBuilder self, Action todo) 167 | { 168 | self.AddStep(todo); 169 | return self; 170 | } 171 | 172 | public static IFlowBuilder Then(this IFlowBuilder self, Action todo) 173 | { 174 | self.AddStep(todo); 175 | return self; 176 | } 177 | 178 | public static IFlowBuilder Then(this IFlowBuilder self, Action todo) 179 | { 180 | self.AddStep(todo); 181 | return self; 182 | } 183 | 184 | public static IFlowBuilder Then(this IFlowBuilder self, Action todo) 185 | { 186 | self.AddStep(todo); 187 | return self; 188 | } 189 | 190 | public static IFlowBuilder Then(this IFlowBuilder self, Action todo) 191 | { 192 | self.AddStep(todo); 193 | return self; 194 | } 195 | 196 | public static IFlowBuilder Then(this IFlowBuilder self, Action todo) 197 | { 198 | self.AddStep(todo); 199 | return self; 200 | } 201 | 202 | public static IFlowBuilder Then(this IFlowBuilder self, Action todo) 203 | { 204 | self.AddStep(todo); 205 | return self; 206 | } 207 | 208 | public static IFlowBuilder Then(this IFlowBuilder self, Action todo) 209 | { 210 | self.AddStep(todo); 211 | return self; 212 | } 213 | 214 | public static IFlowBuilder Then(this IFlowBuilder self, Action todo) 215 | { 216 | self.AddStep(todo); 217 | return self; 218 | } 219 | 220 | public static IFlowBuilder Then(this IFlowBuilder self, Action todo) 221 | { 222 | self.AddStep(todo); 223 | return self; 224 | } 225 | 226 | public static IFlowBuilder Then(this IFlowBuilder self, Action todo) 227 | { 228 | self.AddStep(todo); 229 | return self; 230 | } 231 | 232 | public static IFlowBuilder Then(this IFlowBuilder self, Action todo) 233 | { 234 | self.AddStep(todo); 235 | return self; 236 | } 237 | 238 | #endregion 239 | 240 | #region Then Func 241 | 242 | public static IFlowBuilder Then(this IFlowBuilder self, Func todo) 243 | { 244 | self.AddStep(todo); 245 | return self; 246 | } 247 | 248 | public static IFlowBuilder Then(this IFlowBuilder self, Func todo) 249 | { 250 | self.AddStep(todo); 251 | return self; 252 | } 253 | 254 | public static IFlowBuilder Then(this IFlowBuilder self, Func todo) 255 | { 256 | self.AddStep(todo); 257 | return self; 258 | } 259 | 260 | public static IFlowBuilder Then(this IFlowBuilder self, Func todo) 261 | { 262 | self.AddStep(todo); 263 | return self; 264 | } 265 | 266 | public static IFlowBuilder Then(this IFlowBuilder self, Func todo) 267 | { 268 | self.AddStep(todo); 269 | return self; 270 | } 271 | 272 | public static IFlowBuilder Then(this IFlowBuilder self, Func todo) 273 | { 274 | self.AddStep(todo); 275 | return self; 276 | } 277 | 278 | public static IFlowBuilder Then(this IFlowBuilder self, Func todo) 279 | { 280 | self.AddStep(todo); 281 | return self; 282 | } 283 | 284 | public static IFlowBuilder Then(this IFlowBuilder self, Func todo) 285 | { 286 | self.AddStep(todo); 287 | return self; 288 | } 289 | 290 | public static IFlowBuilder Then(this IFlowBuilder self, Func todo) 291 | { 292 | self.AddStep(todo); 293 | return self; 294 | } 295 | 296 | public static IFlowBuilder Then(this IFlowBuilder self, Func todo) 297 | { 298 | self.AddStep(todo); 299 | return self; 300 | } 301 | 302 | public static IFlowBuilder Then(this IFlowBuilder self, Func todo) 303 | { 304 | self.AddStep(todo); 305 | return self; 306 | } 307 | 308 | public static IFlowBuilder Then(this IFlowBuilder self, Func todo) 309 | { 310 | self.AddStep(todo); 311 | return self; 312 | } 313 | 314 | public static IFlowBuilder Then(this IFlowBuilder self, Func todo) 315 | { 316 | self.AddStep(todo); 317 | return self; 318 | } 319 | 320 | public static IFlowBuilder Then(this IFlowBuilder self, Func todo) 321 | { 322 | self.AddStep(todo); 323 | return self; 324 | } 325 | 326 | public static IFlowBuilder Then(this IFlowBuilder self, Func todo) 327 | { 328 | self.AddStep(todo); 329 | return self; 330 | } 331 | 332 | public static IFlowBuilder Then(this IFlowBuilder self, Func todo) 333 | { 334 | self.AddStep(todo); 335 | return self; 336 | } 337 | 338 | public static IFlowBuilder Then(this IFlowBuilder self, Func todo) 339 | { 340 | self.AddStep(todo); 341 | return self; 342 | } 343 | 344 | #endregion 345 | 346 | #region PackedThen 347 | 348 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func<(TR1, TR2)> todo) 349 | { 350 | self.AddStepPacked(todo); 351 | return self; 352 | } 353 | 354 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 355 | { 356 | self.AddStepPacked(todo); 357 | return self; 358 | } 359 | 360 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 361 | { 362 | self.AddStepPacked(todo); 363 | return self; 364 | } 365 | 366 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 367 | { 368 | self.AddStepPacked(todo); 369 | return self; 370 | } 371 | 372 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 373 | { 374 | self.AddStepPacked(todo); 375 | return self; 376 | } 377 | 378 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 379 | { 380 | self.AddStepPacked(todo); 381 | return self; 382 | } 383 | 384 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 385 | { 386 | self.AddStepPacked(todo); 387 | return self; 388 | } 389 | 390 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 391 | { 392 | self.AddStepPacked(todo); 393 | return self; 394 | } 395 | 396 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 397 | { 398 | self.AddStepPacked(todo); 399 | return self; 400 | } 401 | 402 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 403 | { 404 | self.AddStepPacked(todo); 405 | return self; 406 | } 407 | 408 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 409 | { 410 | self.AddStepPacked(todo); 411 | return self; 412 | } 413 | 414 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 415 | { 416 | self.AddStepPacked(todo); 417 | return self; 418 | } 419 | 420 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 421 | { 422 | self.AddStepPacked(todo); 423 | return self; 424 | } 425 | 426 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 427 | { 428 | self.AddStepPacked(todo); 429 | return self; 430 | } 431 | 432 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 433 | { 434 | self.AddStepPacked(todo); 435 | return self; 436 | } 437 | 438 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 439 | { 440 | self.AddStepPacked(todo); 441 | return self; 442 | } 443 | 444 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 445 | { 446 | self.AddStepPacked(todo); 447 | return self; 448 | } 449 | 450 | 451 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func<(TR1, TR2, TR3)> todo) 452 | { 453 | self.AddStepPacked(todo); 454 | return self; 455 | } 456 | 457 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 458 | { 459 | self.AddStepPacked(todo); 460 | return self; 461 | } 462 | 463 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 464 | { 465 | self.AddStepPacked(todo); 466 | return self; 467 | } 468 | 469 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 470 | { 471 | self.AddStepPacked(todo); 472 | return self; 473 | } 474 | 475 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 476 | { 477 | self.AddStepPacked(todo); 478 | return self; 479 | } 480 | 481 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 482 | { 483 | self.AddStepPacked(todo); 484 | return self; 485 | } 486 | 487 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 488 | { 489 | self.AddStepPacked(todo); 490 | return self; 491 | } 492 | 493 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 494 | { 495 | self.AddStepPacked(todo); 496 | return self; 497 | } 498 | 499 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 500 | { 501 | self.AddStepPacked(todo); 502 | return self; 503 | } 504 | 505 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 506 | { 507 | self.AddStepPacked(todo); 508 | return self; 509 | } 510 | 511 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 512 | { 513 | self.AddStepPacked(todo); 514 | return self; 515 | } 516 | 517 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 518 | { 519 | self.AddStepPacked(todo); 520 | return self; 521 | } 522 | 523 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 524 | { 525 | self.AddStepPacked(todo); 526 | return self; 527 | } 528 | 529 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 530 | { 531 | self.AddStepPacked(todo); 532 | return self; 533 | } 534 | 535 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 536 | { 537 | self.AddStepPacked(todo); 538 | return self; 539 | } 540 | 541 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 542 | { 543 | self.AddStepPacked(todo); 544 | return self; 545 | } 546 | 547 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 548 | { 549 | self.AddStepPacked(todo); 550 | return self; 551 | } 552 | 553 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func<(TR1, TR2, TR3, TR4)> todo) 554 | { 555 | self.AddStepPacked(todo); 556 | return self; 557 | } 558 | 559 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 560 | { 561 | self.AddStepPacked(todo); 562 | return self; 563 | } 564 | 565 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 566 | { 567 | self.AddStepPacked(todo); 568 | return self; 569 | } 570 | 571 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 572 | { 573 | self.AddStepPacked(todo); 574 | return self; 575 | } 576 | 577 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 578 | { 579 | self.AddStepPacked(todo); 580 | return self; 581 | } 582 | 583 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 584 | { 585 | self.AddStepPacked(todo); 586 | return self; 587 | } 588 | 589 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 590 | { 591 | self.AddStepPacked(todo); 592 | return self; 593 | } 594 | 595 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 596 | { 597 | self.AddStepPacked(todo); 598 | return self; 599 | } 600 | 601 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 602 | { 603 | self.AddStepPacked(todo); 604 | return self; 605 | } 606 | 607 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 608 | { 609 | self.AddStepPacked(todo); 610 | return self; 611 | } 612 | 613 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 614 | { 615 | self.AddStepPacked(todo); 616 | return self; 617 | } 618 | 619 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 620 | { 621 | self.AddStepPacked(todo); 622 | return self; 623 | } 624 | 625 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 626 | { 627 | self.AddStepPacked(todo); 628 | return self; 629 | } 630 | 631 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 632 | { 633 | self.AddStepPacked(todo); 634 | return self; 635 | } 636 | 637 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 638 | { 639 | self.AddStepPacked(todo); 640 | return self; 641 | } 642 | 643 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 644 | { 645 | self.AddStepPacked(todo); 646 | return self; 647 | } 648 | 649 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 650 | { 651 | self.AddStepPacked(todo); 652 | return self; 653 | } 654 | 655 | 656 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func<(TR1, TR2, TR3, TR4, TR5)> todo) 657 | { 658 | self.AddStepPacked(todo); 659 | return self; 660 | } 661 | 662 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 663 | { 664 | self.AddStepPacked(todo); 665 | return self; 666 | } 667 | 668 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 669 | { 670 | self.AddStepPacked(todo); 671 | return self; 672 | } 673 | 674 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 675 | { 676 | self.AddStepPacked(todo); 677 | return self; 678 | } 679 | 680 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 681 | { 682 | self.AddStepPacked(todo); 683 | return self; 684 | } 685 | 686 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 687 | { 688 | self.AddStepPacked(todo); 689 | return self; 690 | } 691 | 692 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 693 | { 694 | self.AddStepPacked(todo); 695 | return self; 696 | } 697 | 698 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 699 | { 700 | self.AddStepPacked(todo); 701 | return self; 702 | } 703 | 704 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 705 | { 706 | self.AddStepPacked(todo); 707 | return self; 708 | } 709 | 710 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 711 | { 712 | self.AddStepPacked(todo); 713 | return self; 714 | } 715 | 716 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 717 | { 718 | self.AddStepPacked(todo); 719 | return self; 720 | } 721 | 722 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 723 | { 724 | self.AddStepPacked(todo); 725 | return self; 726 | } 727 | 728 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 729 | { 730 | self.AddStepPacked(todo); 731 | return self; 732 | } 733 | 734 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 735 | { 736 | self.AddStepPacked(todo); 737 | return self; 738 | } 739 | 740 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 741 | { 742 | self.AddStepPacked(todo); 743 | return self; 744 | } 745 | 746 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 747 | { 748 | self.AddStepPacked(todo); 749 | return self; 750 | } 751 | 752 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 753 | { 754 | self.AddStepPacked(todo); 755 | return self; 756 | } 757 | 758 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func<(TR1, TR2, TR3, TR4, TR5, TR6)> todo) 759 | { 760 | self.AddStepPacked(todo); 761 | return self; 762 | } 763 | 764 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 765 | { 766 | self.AddStepPacked(todo); 767 | return self; 768 | } 769 | 770 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 771 | { 772 | self.AddStepPacked(todo); 773 | return self; 774 | } 775 | 776 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 777 | { 778 | self.AddStepPacked(todo); 779 | return self; 780 | } 781 | 782 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 783 | { 784 | self.AddStepPacked(todo); 785 | return self; 786 | } 787 | 788 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 789 | { 790 | self.AddStepPacked(todo); 791 | return self; 792 | } 793 | 794 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 795 | { 796 | self.AddStepPacked(todo); 797 | return self; 798 | } 799 | 800 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 801 | { 802 | self.AddStepPacked(todo); 803 | return self; 804 | } 805 | 806 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 807 | { 808 | self.AddStepPacked(todo); 809 | return self; 810 | } 811 | 812 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 813 | { 814 | self.AddStepPacked(todo); 815 | return self; 816 | } 817 | 818 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 819 | { 820 | self.AddStepPacked(todo); 821 | return self; 822 | } 823 | 824 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 825 | { 826 | self.AddStepPacked(todo); 827 | return self; 828 | } 829 | 830 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 831 | { 832 | self.AddStepPacked(todo); 833 | return self; 834 | } 835 | 836 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 837 | { 838 | self.AddStepPacked(todo); 839 | return self; 840 | } 841 | 842 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 843 | { 844 | self.AddStepPacked(todo); 845 | return self; 846 | } 847 | 848 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 849 | { 850 | self.AddStepPacked(todo); 851 | return self; 852 | } 853 | 854 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 855 | { 856 | self.AddStepPacked(todo); 857 | return self; 858 | } 859 | 860 | 861 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func<(TR1, TR2, TR3, TR4, TR5, TR6, TR7)> todo) 862 | { 863 | self.AddStepPacked(todo); 864 | return self; 865 | } 866 | 867 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 868 | { 869 | self.AddStepPacked(todo); 870 | return self; 871 | } 872 | 873 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 874 | { 875 | self.AddStepPacked(todo); 876 | return self; 877 | } 878 | 879 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 880 | { 881 | self.AddStepPacked(todo); 882 | return self; 883 | } 884 | 885 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 886 | { 887 | self.AddStepPacked(todo); 888 | return self; 889 | } 890 | 891 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 892 | { 893 | self.AddStepPacked(todo); 894 | return self; 895 | } 896 | 897 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 898 | { 899 | self.AddStepPacked(todo); 900 | return self; 901 | } 902 | 903 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 904 | { 905 | self.AddStepPacked(todo); 906 | return self; 907 | } 908 | 909 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 910 | { 911 | self.AddStepPacked(todo); 912 | return self; 913 | } 914 | 915 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 916 | { 917 | self.AddStepPacked(todo); 918 | return self; 919 | } 920 | 921 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 922 | { 923 | self.AddStepPacked(todo); 924 | return self; 925 | } 926 | 927 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 928 | { 929 | self.AddStepPacked(todo); 930 | return self; 931 | } 932 | 933 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 934 | { 935 | self.AddStepPacked(todo); 936 | return self; 937 | } 938 | 939 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 940 | { 941 | self.AddStepPacked(todo); 942 | return self; 943 | } 944 | 945 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 946 | { 947 | self.AddStepPacked(todo); 948 | return self; 949 | } 950 | 951 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 952 | { 953 | self.AddStepPacked(todo); 954 | return self; 955 | } 956 | 957 | public static IFlowBuilder PackedThen(this IFlowBuilder self, Func todo) 958 | { 959 | self.AddStepPacked(todo); 960 | return self; 961 | } 962 | 963 | #endregion 964 | 965 | } 966 | } -------------------------------------------------------------------------------- /FunctionFlow/FunctionFlow.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netstandard2.1 4 | 40 5 | 15.0 6 | D:\FunctionFlow\Backup\ 7 | Prototypist.FunctionFlow 8 | False 9 | Prototypist 10 | Prototypist 11 | FunctionFlow 12 | A library for conposing small algrothims 13 | false 14 | Prototypist.pfx 15 | false 16 | FunctionFlow 17 | 2018 Colin Wielga 18 | https://github.com/Prototypist1/FunctionFlow 19 | 20 | https://github.com/Prototypist1/FunctionFlow 21 | git 22 | Prototypist.FunctionFlow 23 | - 24 | 1.0.0.2 25 | False 26 | MIT 27 | icon.png 28 | 29 | 30 | 7.3 31 | 32 | 33 | 7.3 34 | 35 | 36 | 37 | True 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /FunctionFlow/IFlowBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using System.Reflection; 4 | 5 | namespace Prototypist.FunctionFlow 6 | { 7 | public interface IFlowBuilder 8 | { 9 | bool Parallel { get; set; } 10 | 11 | void SetConstant(T t); 12 | void SetSource(T t) where T : Delegate; 13 | void AddStep(Delegate expression); 14 | void AddStepPacked(Delegate expression); 15 | void AddStepPacked(Delegate expression); 16 | void AddStepPacked(Delegate expression); 17 | void AddStepPacked(Delegate expression); 18 | void AddStepPacked(Delegate expression); 19 | void AddStepPacked(Delegate expression); 20 | void SetContainer(IContainer container); 21 | 22 | T Build() where T : Delegate; 23 | void Run(params object[] inputs); 24 | T Run(params object[] inputs); 25 | (T1, T2) Run(params object[] inputs); 26 | (T1, T2, T3) Run(params object[] inputs); 27 | (T1, T2, T3, T4) Run(params object[] inputs); 28 | (T1, T2, T3, T4, T5) Run(params object[] inputs); 29 | (T1, T2, T3, T4, T5, T6) Run(params object[] inputs); 30 | (T1, T2, T3, T4, T5, T6, T7) Run(params object[] inputs); 31 | 32 | } 33 | } -------------------------------------------------------------------------------- /FunctionFlow/TypeExtensions.cs: -------------------------------------------------------------------------------- 1 |  2 | using System; 3 | using System.Linq; 4 | 5 | namespace Prototypist.FunctionFlow 6 | { 7 | internal static class TypeExtensions 8 | { 9 | 10 | public static bool IsAction(this Type funcType) 11 | { 12 | return funcType == typeof(Action) || actionsArray.Value.Contains(funcType.GetGenericTypeDefinition()); 13 | } 14 | 15 | public static bool IsFunc(this Type funcType) 16 | { 17 | return funcType.IsGenericType && functionsArray.Value.Contains(funcType.GetGenericTypeDefinition()); 18 | } 19 | 20 | public static Type FuncReturnOrNull(this Type funcType) 21 | { 22 | if (funcType.IsFunc()) 23 | { 24 | return funcType.GetGenericArguments().Last(); 25 | } 26 | else 27 | { 28 | return null; 29 | } 30 | } 31 | 32 | public static Type[] Parameters(this Type funcType) 33 | { 34 | if (funcType.IsAction()) 35 | { 36 | return funcType.GetGenericArguments(); 37 | } 38 | else if (funcType.IsFunc()) 39 | { 40 | return funcType.GetGenericArguments().Take(funcType.GetGenericArguments().Count() - 1).ToArray(); 41 | } 42 | throw new Exception($"{funcType} should be an {typeof(Action)} or {typeof(Func<>)}"); 43 | } 44 | 45 | private static readonly Lazy functionsArray = new Lazy(() => new Type[] { 46 | typeof(Func<>), 47 | typeof(Func<,>), 48 | typeof(Func<,,>), 49 | typeof(Func<,,,>), 50 | typeof(Func<,,,,>), 51 | typeof(Func<,,,,,>), 52 | typeof(Func<,,,,,,>), 53 | typeof(Func<,,,,,,,>), 54 | typeof(Func<,,,,,,,,>), 55 | typeof(Func<,,,,,,,,,>), 56 | typeof(Func<,,,,,,,,,,>), 57 | typeof(Func<,,,,,,,,,,,>), 58 | typeof(Func<,,,,,,,,,,,,>), 59 | typeof(Func<,,,,,,,,,,,,,>), 60 | typeof(Func<,,,,,,,,,,,,,,>), 61 | typeof(Func<,,,,,,,,,,,,,,,>), 62 | typeof(Func<,,,,,,,,,,,,,,,,>), 63 | }); 64 | 65 | private static readonly Lazy actionsArray = new Lazy(() => new Type[] { 66 | typeof(Action), 67 | typeof(Action<>), 68 | typeof(Action<,>), 69 | typeof(Action<,,>), 70 | typeof(Action<,,,>), 71 | typeof(Action<,,,,>), 72 | typeof(Action<,,,,,>), 73 | typeof(Action<,,,,,,>), 74 | typeof(Action<,,,,,,,>), 75 | typeof(Action<,,,,,,,,>), 76 | typeof(Action<,,,,,,,,,>), 77 | typeof(Action<,,,,,,,,,,>), 78 | typeof(Action<,,,,,,,,,,,>), 79 | typeof(Action<,,,,,,,,,,,,>), 80 | typeof(Action<,,,,,,,,,,,,,>), 81 | typeof(Action<,,,,,,,,,,,,,,>), 82 | typeof(Action<,,,,,,,,,,,,,,,>), 83 | }); 84 | 85 | } 86 | 87 | 88 | } 89 | -------------------------------------------------------------------------------- /FunctionFlow/WorkItem.cs: -------------------------------------------------------------------------------- 1 |  2 | using System; 3 | 4 | namespace Prototypist.FunctionFlow 5 | { 6 | 7 | public partial class FlowBuilder 8 | { 9 | private class WorkItem 10 | { 11 | public readonly Delegate todo; 12 | public readonly Type[] returnTypes; 13 | 14 | public WorkItem(Delegate todo, Type[] types) 15 | { 16 | this.todo = todo ?? throw new ArgumentNullException(nameof(todo)); 17 | this.returnTypes = types; 18 | } 19 | } 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | *Function Flow* is a library that takes a set of algorithms and wires them together in to a single composite algorithm. 4 | 5 | For example, say your sub algorithms are: chop onions, mix sauce, grate cheese, fry tortillas, assemble, bake, and garnish, and your composite algorithm is making enchiladas. 6 | 7 | ![Enchilada Algorithm](https://raw.githubusercontent.com/Prototypist1/FunctionGraph/master/EnchiladaAlgorithm.png) 8 | 9 | Explicitly composed, the code might look like this: 10 | 11 | ```C# 12 | public Enchilada MakeEnchilada(Oil oil, Spices spices, Flour flour, Broth broth, Tortillas tortillas, Cheese cheese, Onion onion){ 13 | var sauce = Mix(oil, spices, flour, broth); 14 | var browned = Fry(oil, tortillas); 15 | var grated = Grate(cheese); 16 | var assembled = Assemble(sauce, browned, grated); 17 | var almost = Bake(assembled); 18 | var chopped = Chop(onion); 19 | return Garnish(almost, sauce, chopped); 20 | } 21 | ``` 22 | 23 | The thing is, it is pretty easy to wire the sub-algorithms together by matching their input types to output types. An algorithm can determine that the result of `Assemble` must be fed into `Bake` because `Bake` requires an `Assembled` and that is what `Assemble` produces. This generalizes. Run the sub-algorithms in order. For each of the sub-algorithm's inputs, find the youngest item of matching type that was output from one of the previous sub-algorithms or provided as a parameter. This is exactly what Function Flow does. 24 | 25 | Using Function Flow the same code looks like: 26 | 27 | ```C# 28 | public Enchilada MakeEnchilada(Oil oil, Spices spices, Flour flour, Broth broth, Tortillas tortillas, Cheese cheese, Onion onion){ 29 | return new FlowBuilder() 30 | .Then(Mix) 31 | .Then(Fry) 32 | .Then(Grate) 33 | .Then(Assemble) 34 | .Then(Bake) 35 | .Then(Chop) 36 | .Then(Garnish) 37 | .Run(oil, spices, flour, broth, tortillas, cheese, onion); 38 | } 39 | ``` 40 | 41 | So, fewer characters, a little less mental strain, and slightly more readable code, at the cost of some performance and control. 42 | 43 | ## Features 44 | 45 | ### Parallelism 46 | 47 | Function Flow can automatically parallelize your flow. Much like automatic wiring, automatic parallelization is pretty simple. Each sub-algorithm is run as soon as all the sub-algorithms that produce its inputs are finished. 48 | 49 | To make a flow parallel, just use the `RunInParallel` method. 50 | 51 | ```C# 52 | public Enchilada MakeEnchilada(Oil oil, Spices spices, Flour flour, Broth broth, Tortillas tortillas, Cheese cheese, Onion onion){ 53 | return new FlowBuilder() 54 | .RunInParallel() 55 | .Then(Mix) 56 | .Then(Fry) 57 | .Then(Grate) 58 | .Then(Assemble) 59 | .Then(Bake) 60 | .Then(Chop) 61 | .Then(Garnish) 62 | .Run(oil, spices, flour, broth, tortillas, cheese, onion); 63 | } 64 | ``` 65 | 66 | ### Dependency Injection Support 67 | 68 | Function Flow pairs very well with dependency injection. It allows you to not worry about feeding your flow with the right inputs. Instead, the flow just pulls whatever it needs from the container. 69 | 70 | The container is used as a fallback when wiring functions together. Required inputs that are not produced by any sub algorithms or included as parameters are pulled from the container. 71 | 72 | To add a container, wrap your container of choice in the `IContainer` interface and pass it in with the `Container` method. 73 | 74 | ```C# 75 | public Enchilada MakeEnchilada(IContainer pantry){ 76 | return new FlowBuilder() 77 | .Container(pantry) 78 | .Then(Mix) 79 | .Then(Fry) 80 | .Then(Grate) 81 | .Then(Assemble) 82 | .Then(Bake) 83 | .Then(Chop) 84 | .Then(Garnish) 85 | .Run(); 86 | } 87 | ``` 88 | 89 | ### Multiple Returns 90 | 91 | Function Flow uses tuples to handle multiple returns. `PackedThen` takes a function that returns multiple items 'packed' as a tuple. All the returned tuple's members are available to subsequent steps, but the tuple as a whole is not. (If you want the tuple to be available, use `Then`.) 92 | 93 | To return multiple items from the flow, add generic parameters to `Run`. The youngest items of the types requested are returned in a tuple. 94 | 95 | ```C# 96 | var (name, value) = new FlowBuilder() 97 | .PackedThen(() => ("Time", 1282368345)) 98 | .Then((string s)=> "Current "+ s) 99 | .Run(); 100 | ``` 101 | ### Strong Typing 102 | 103 | One of the down sides of function flow is, the flow is not verified at compile time. If one of your steps requires something that is not in the flow you will be blissfully unware until runtime. I have make an attempt to rectify this using generics and extension methods. The "strongly typed" form of `FlowBuilder` is `IStrongFlow<...items...>`. Instead of `Then` it uses `Add` and `Update`. A `StrongFlow` simulates strong typing by only having extension methods for functions whose inputs it contains. For example, `StrongFlow` only has extension methods for methods of the form `(int i) => ...`, `(string s) => ...`, `(int i, string s) => ...` and, `(string s, int i)=> ...`. 104 | 105 | ```C# 106 | var year = new StrongFlow() 107 | .Add(()=>5) // now a IStrongFlow 108 | .Update((int i) => i +1) // still IStrongFlow 109 | .Add(()=>"177") // now a IStrongFlow 110 | .Update((string s, int i) => s + i) // still IStrongFlow 111 | .Run(); 112 | } 113 | ``` 114 | 115 | #### Strong Typing Limitations 116 | 117 | - No support for containers 118 | - `Add` and `Update` must be specifically specified 119 | - Can only track up to 12 items 120 | 121 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Prototypist1/FunctionFlow/b85de0e571a2e92c5b4773f4fb5da6b6c09f8abf/icon.png -------------------------------------------------------------------------------- /icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 31 | 39 | 44 | 45 | 55 | 65 | 75 | 85 | 95 | 105 | 115 | 125 | 135 | 145 | 155 | 165 | 175 | 185 | 195 | 205 | 215 | 225 | 235 | 245 | 255 | 265 | 275 | 285 | 295 | 305 | 315 | 325 | 335 | 345 | 355 | 365 | 375 | 385 | 395 | 405 | 415 | 425 | 435 | 445 | 455 | 465 | 475 | 485 | 495 | 505 | 515 | 525 | 535 | 545 | 555 | 565 | 575 | 585 | 595 | 605 | 615 | 625 | 635 | 645 | 655 | 665 | 675 | 685 | 695 | 705 | 706 | 725 | 728 | 729 | 731 | 732 | 734 | image/svg+xml 735 | 737 | 738 | 739 | 740 | 744 | 748 | 757 | 766 | 775 | 784 | 793 | 802 | 811 | 820 | 829 | 838 | 847 | 856 | 865 | 874 | 883 | 892 | 901 | 910 | 919 | 928 | 937 | 946 | 955 | 964 | 965 | --------------------------------------------------------------------------------